@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
package/logger.d.ts
ADDED
package/logger.js
ADDED
package/mfe.d.ts
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export interface MfeInputs {
|
|
2
|
+
side: 'long' | 'short';
|
|
3
|
+
entryPrice: number;
|
|
4
|
+
/** Stop at fill, frozen. If undefined, MFE-in-R is undefined. */
|
|
5
|
+
originalStopPrice?: number;
|
|
6
|
+
/** Latest mark observation. */
|
|
7
|
+
markPrice: number;
|
|
8
|
+
/** Prior peak from the last update (undefined on first call). */
|
|
9
|
+
priorPeakPrice?: number;
|
|
10
|
+
}
|
|
11
|
+
export interface MfeOutputs {
|
|
12
|
+
/** New peak price after observing this mark. */
|
|
13
|
+
mfePeakPrice: number;
|
|
14
|
+
/** Peak in R-multiples. Undefined if originalStopPrice missing. */
|
|
15
|
+
mfeR?: number;
|
|
16
|
+
/** Give-back ratio in [0, 1+]. Undefined if peak is not favourable yet. */
|
|
17
|
+
giveBackRatio?: number;
|
|
18
|
+
}
|
|
19
|
+
/** One-shot update: given current state + new mark, return the new peak,
|
|
20
|
+
* R-multiple, and give-back. Pure function — caller persists the result. */
|
|
21
|
+
export declare function updateMfe(input: MfeInputs): MfeOutputs;
|
package/mfe.js
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
// MFE (Max Favourable Excursion) tracking — pure math, no I/O.
|
|
2
|
+
//
|
|
3
|
+
// Component C of POSITION_MANAGEMENT_PLAN.md. The agent today has no felt
|
|
4
|
+
// sense of "I am leaking edge" because the heartbeat snapshot doesn't carry
|
|
5
|
+
// MFE or give-back. These helpers compute both from the entry-time stop and
|
|
6
|
+
// the running peak, and are wired into the paper simulator's mark-tick path
|
|
7
|
+
// and the live adapter's getPositions decoration.
|
|
8
|
+
//
|
|
9
|
+
// Conventions (confirmed with operator 2026-04-27):
|
|
10
|
+
// - 1R is denominated against the ORIGINAL stop at fill, frozen for the life
|
|
11
|
+
// of the position. Ratcheting the live stopPrice does not change R.
|
|
12
|
+
// - Without an originalStopPrice (e.g. paper position opened without a stop),
|
|
13
|
+
// `mfeR` and `giveBackRatio` are reported as `undefined` rather than
|
|
14
|
+
// fabricated from a default risk unit.
|
|
15
|
+
// - Sign is always favourable: long positions track the highest mark seen,
|
|
16
|
+
// short positions track the lowest. `mfeR` is non-negative.
|
|
17
|
+
//
|
|
18
|
+
// Edge cases handled:
|
|
19
|
+
// - Pre-entry tick (entry price not yet observed): caller should not invoke.
|
|
20
|
+
// - Adverse-only excursion (MFE never positive): `giveBackRatio` is left
|
|
21
|
+
// `undefined` because the ratio is meaningless when peak <= 0.
|
|
22
|
+
// - Tied peak (mark equals prior peak): treated as the peak; give-back stays 0.
|
|
23
|
+
/** Compute the favourable distance (always >= 0) between mark and entry. */
|
|
24
|
+
function favourableDelta(side, entry, mark) {
|
|
25
|
+
return side === 'long' ? mark - entry : entry - mark;
|
|
26
|
+
}
|
|
27
|
+
/** True if `candidate` is at least as favourable as `incumbent` for this side. */
|
|
28
|
+
function atLeastAsFavourable(side, candidate, incumbent) {
|
|
29
|
+
return side === 'long' ? candidate >= incumbent : candidate <= incumbent;
|
|
30
|
+
}
|
|
31
|
+
/** One-shot update: given current state + new mark, return the new peak,
|
|
32
|
+
* R-multiple, and give-back. Pure function — caller persists the result. */
|
|
33
|
+
export function updateMfe(input) {
|
|
34
|
+
const { side, entryPrice, originalStopPrice, markPrice, priorPeakPrice } = input;
|
|
35
|
+
if (!Number.isFinite(markPrice) || !Number.isFinite(entryPrice)) {
|
|
36
|
+
// Caller error — return prior peak unchanged.
|
|
37
|
+
return {
|
|
38
|
+
mfePeakPrice: priorPeakPrice ?? entryPrice,
|
|
39
|
+
mfeR: undefined,
|
|
40
|
+
giveBackRatio: undefined,
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
// Seed peak to entry on first observation, then ratchet only in the
|
|
44
|
+
// favourable direction.
|
|
45
|
+
const seedPeak = priorPeakPrice ?? entryPrice;
|
|
46
|
+
const newPeak = atLeastAsFavourable(side, markPrice, seedPeak) ? markPrice : seedPeak;
|
|
47
|
+
// Compute R if we have an original stop. R denominator is the at-fill
|
|
48
|
+
// distance from entry to stop; undefined or non-positive denominator means
|
|
49
|
+
// we can't report R.
|
|
50
|
+
const rDenominator = originalStopPrice !== undefined && Number.isFinite(originalStopPrice)
|
|
51
|
+
? Math.abs(entryPrice - originalStopPrice)
|
|
52
|
+
: 0;
|
|
53
|
+
if (rDenominator <= 0) {
|
|
54
|
+
return { mfePeakPrice: newPeak, mfeR: undefined, giveBackRatio: undefined };
|
|
55
|
+
}
|
|
56
|
+
const peakFavourableDelta = favourableDelta(side, entryPrice, newPeak);
|
|
57
|
+
const currentFavourableDelta = favourableDelta(side, entryPrice, markPrice);
|
|
58
|
+
// mfeR is always non-negative — clamp adverse-only positions to 0.
|
|
59
|
+
const mfeR = Math.max(0, peakFavourableDelta / rDenominator);
|
|
60
|
+
// Give-back is only meaningful once peak has actually moved into profit.
|
|
61
|
+
// Negative peak (price went the wrong way and never recovered) → undefined.
|
|
62
|
+
let giveBackRatio;
|
|
63
|
+
if (peakFavourableDelta > 0) {
|
|
64
|
+
const givenBack = peakFavourableDelta - currentFavourableDelta;
|
|
65
|
+
giveBackRatio = Math.max(0, givenBack / peakFavourableDelta);
|
|
66
|
+
}
|
|
67
|
+
return { mfePeakPrice: newPeak, mfeR, giveBackRatio };
|
|
68
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { validateModeTransition, modeRequiresCredentials, type ModeTransitionResult, } from '@reefclaw/shared';
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import type { IExchangeAdapter } from '../exchange-adapter.js';
|
|
2
|
+
import type { ExchangeConfig, TradingMode } from '../types.js';
|
|
3
|
+
import type { ExchangeSimulator } from '../simulator/exchange-simulator.js';
|
|
4
|
+
import type { PaperMarketFeed } from '../simulator/paper-market-feed.js';
|
|
5
|
+
import { PositionWatcher } from '../live/stop-watcher.js';
|
|
6
|
+
export interface MicroLiveConfig {
|
|
7
|
+
sizeCapPercent?: number;
|
|
8
|
+
maxPositionUSDT?: number;
|
|
9
|
+
}
|
|
10
|
+
export interface BuildAdapterInput {
|
|
11
|
+
mode: TradingMode;
|
|
12
|
+
exchange: ExchangeConfig | null;
|
|
13
|
+
microLive?: MicroLiveConfig;
|
|
14
|
+
simulator: ExchangeSimulator;
|
|
15
|
+
}
|
|
16
|
+
/** Pure-ish factory: builds an adapter for the requested mode.
|
|
17
|
+
* Falls back to PaperAdapter if a live mode is requested without credentials —
|
|
18
|
+
* callers should pre-validate via `modeRequiresCredentials`, but this
|
|
19
|
+
* defense-in-depth prevents a crash if validation is bypassed. */
|
|
20
|
+
export declare function buildAdapter(input: BuildAdapterInput): IExchangeAdapter;
|
|
21
|
+
/**
|
|
22
|
+
* Mutable runtime holder. Held once per plugin registration.
|
|
23
|
+
*
|
|
24
|
+
* `adapterBox.adapter` is the indirection point: tool closures read it at
|
|
25
|
+
* execute time so a swap is visible without re-registering tools.
|
|
26
|
+
*/
|
|
27
|
+
export declare class PluginRuntime {
|
|
28
|
+
/** Box object so existing `const adapterDeps = { adapter }` patterns still
|
|
29
|
+
* work via `adapterDeps.adapter = runtime.adapterBox.adapter`. In practice
|
|
30
|
+
* we pass the runtime itself and read `runtime.adapter` at call sites. */
|
|
31
|
+
private _adapter;
|
|
32
|
+
private _mode;
|
|
33
|
+
private _stopWatcher;
|
|
34
|
+
private readonly simulator;
|
|
35
|
+
/** Paper-only background market-data feed (refreshes simulator marks so
|
|
36
|
+
* stops fire and NAV/uPnL stay live for non-dashboard symbols). Lives for
|
|
37
|
+
* the runtime's lifetime; started in paper, stopped in live. */
|
|
38
|
+
private readonly _marketFeed;
|
|
39
|
+
/** Reconnect is serialized — a second caller waits for the first to finish
|
|
40
|
+
* so we never tear down an adapter that's mid-rebuild. */
|
|
41
|
+
private reconnectInFlight;
|
|
42
|
+
constructor(initial: {
|
|
43
|
+
adapter: IExchangeAdapter;
|
|
44
|
+
mode: TradingMode;
|
|
45
|
+
simulator: ExchangeSimulator;
|
|
46
|
+
stopWatcher?: PositionWatcher | null;
|
|
47
|
+
marketFeed?: PaperMarketFeed | null;
|
|
48
|
+
});
|
|
49
|
+
get adapter(): IExchangeAdapter;
|
|
50
|
+
get mode(): TradingMode;
|
|
51
|
+
get stopWatcher(): PositionWatcher | null;
|
|
52
|
+
get marketFeed(): PaperMarketFeed | null;
|
|
53
|
+
/**
|
|
54
|
+
* Swap the current adapter for a new one built from `next`. The old
|
|
55
|
+
* adapter's background loops are stopped before the new one is wired in.
|
|
56
|
+
*
|
|
57
|
+
* `deps.adapterDeps` is the tool-dependency object used by trading tools —
|
|
58
|
+
* its `.adapter` reference is updated in place so tools see the new adapter
|
|
59
|
+
* without needing re-registration.
|
|
60
|
+
*/
|
|
61
|
+
reconnect(next: {
|
|
62
|
+
mode: TradingMode;
|
|
63
|
+
exchange: ExchangeConfig | null;
|
|
64
|
+
microLive?: MicroLiveConfig;
|
|
65
|
+
}, deps: {
|
|
66
|
+
adapterDeps: {
|
|
67
|
+
adapter: IExchangeAdapter;
|
|
68
|
+
};
|
|
69
|
+
}): Promise<void>;
|
|
70
|
+
private doReconnect;
|
|
71
|
+
}
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
// Plugin runtime holder — the mutable state that survives across a
|
|
2
|
+
// credential/mode swap. The rest of index.ts closes over this object
|
|
3
|
+
// instead of over `let` bindings, so a reconnect can atomically:
|
|
4
|
+
//
|
|
5
|
+
// 1. Stop the live adapter's background loops (order poller + reconciler)
|
|
6
|
+
// 2. Stop the stop-watcher
|
|
7
|
+
// 3. Build a new adapter from updated config
|
|
8
|
+
// 4. Swap `runtime.adapter` in place (closures see the new value)
|
|
9
|
+
// 5. Start a new stop-watcher pointed at the new adapter
|
|
10
|
+
//
|
|
11
|
+
// Doing this without dropping tool registrations or rebuilding the whole
|
|
12
|
+
// plugin is what makes the dashboard "Reconnect Exchange" button feel
|
|
13
|
+
// instant — no gateway restart required.
|
|
14
|
+
import { PaperAdapter } from '../paper-adapter.js';
|
|
15
|
+
import { LiveAdapter } from '../live/live-adapter.js';
|
|
16
|
+
import { PositionWatcher } from '../live/stop-watcher.js';
|
|
17
|
+
import { loadBracketMode } from '../config/brackets-config.js';
|
|
18
|
+
import { logger, formatError } from '../logger.js';
|
|
19
|
+
const TAG = 'plugin-runtime';
|
|
20
|
+
/** Pure-ish factory: builds an adapter for the requested mode.
|
|
21
|
+
* Falls back to PaperAdapter if a live mode is requested without credentials —
|
|
22
|
+
* callers should pre-validate via `modeRequiresCredentials`, but this
|
|
23
|
+
* defense-in-depth prevents a crash if validation is bypassed. */
|
|
24
|
+
export function buildAdapter(input) {
|
|
25
|
+
const { mode, exchange, microLive, simulator } = input;
|
|
26
|
+
if (mode === 'PAPER' || mode === 'SHADOW') {
|
|
27
|
+
// Shadow mode uses a paper adapter for execution; the separate
|
|
28
|
+
// ShadowTracker wraps a BinancePrivateApi for real-balance comparison.
|
|
29
|
+
return new PaperAdapter(simulator);
|
|
30
|
+
}
|
|
31
|
+
if (!exchange) {
|
|
32
|
+
logger.warn(TAG, `${mode} requested without credentials — falling back to PAPER`);
|
|
33
|
+
return new PaperAdapter(simulator);
|
|
34
|
+
}
|
|
35
|
+
// Read bracket-mode from plugin-config at adapter build time so a config
|
|
36
|
+
// flip + "Reconnect Exchange" pattern picks up the new mode on the next swap.
|
|
37
|
+
const bracketMode = loadBracketMode();
|
|
38
|
+
return new LiveAdapter(exchange, mode, microLive, bracketMode);
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Mutable runtime holder. Held once per plugin registration.
|
|
42
|
+
*
|
|
43
|
+
* `adapterBox.adapter` is the indirection point: tool closures read it at
|
|
44
|
+
* execute time so a swap is visible without re-registering tools.
|
|
45
|
+
*/
|
|
46
|
+
export class PluginRuntime {
|
|
47
|
+
/** Box object so existing `const adapterDeps = { adapter }` patterns still
|
|
48
|
+
* work via `adapterDeps.adapter = runtime.adapterBox.adapter`. In practice
|
|
49
|
+
* we pass the runtime itself and read `runtime.adapter` at call sites. */
|
|
50
|
+
_adapter;
|
|
51
|
+
_mode;
|
|
52
|
+
_stopWatcher = null;
|
|
53
|
+
simulator;
|
|
54
|
+
/** Paper-only background market-data feed (refreshes simulator marks so
|
|
55
|
+
* stops fire and NAV/uPnL stay live for non-dashboard symbols). Lives for
|
|
56
|
+
* the runtime's lifetime; started in paper, stopped in live. */
|
|
57
|
+
_marketFeed;
|
|
58
|
+
/** Reconnect is serialized — a second caller waits for the first to finish
|
|
59
|
+
* so we never tear down an adapter that's mid-rebuild. */
|
|
60
|
+
reconnectInFlight = null;
|
|
61
|
+
constructor(initial) {
|
|
62
|
+
this._adapter = initial.adapter;
|
|
63
|
+
this._mode = initial.mode;
|
|
64
|
+
this.simulator = initial.simulator;
|
|
65
|
+
this._stopWatcher = initial.stopWatcher ?? null;
|
|
66
|
+
this._marketFeed = initial.marketFeed ?? null;
|
|
67
|
+
}
|
|
68
|
+
get adapter() { return this._adapter; }
|
|
69
|
+
get mode() { return this._mode; }
|
|
70
|
+
get stopWatcher() { return this._stopWatcher; }
|
|
71
|
+
get marketFeed() { return this._marketFeed; }
|
|
72
|
+
/**
|
|
73
|
+
* Swap the current adapter for a new one built from `next`. The old
|
|
74
|
+
* adapter's background loops are stopped before the new one is wired in.
|
|
75
|
+
*
|
|
76
|
+
* `deps.adapterDeps` is the tool-dependency object used by trading tools —
|
|
77
|
+
* its `.adapter` reference is updated in place so tools see the new adapter
|
|
78
|
+
* without needing re-registration.
|
|
79
|
+
*/
|
|
80
|
+
async reconnect(next, deps) {
|
|
81
|
+
// Serialize concurrent reconnects.
|
|
82
|
+
if (this.reconnectInFlight) {
|
|
83
|
+
await this.reconnectInFlight;
|
|
84
|
+
}
|
|
85
|
+
const run = this.doReconnect(next, deps);
|
|
86
|
+
this.reconnectInFlight = run.finally(() => {
|
|
87
|
+
this.reconnectInFlight = null;
|
|
88
|
+
});
|
|
89
|
+
await run;
|
|
90
|
+
}
|
|
91
|
+
async doReconnect(next, deps) {
|
|
92
|
+
logger.info(TAG, `Reconnect: ${this._mode} → ${next.mode}`);
|
|
93
|
+
// 1. Stop old stop-watcher.
|
|
94
|
+
if (this._stopWatcher) {
|
|
95
|
+
try {
|
|
96
|
+
this._stopWatcher.stop();
|
|
97
|
+
}
|
|
98
|
+
catch (err) {
|
|
99
|
+
logger.warn(TAG, `stopWatcher.stop failed: ${formatError(err)}`);
|
|
100
|
+
}
|
|
101
|
+
this._stopWatcher = null;
|
|
102
|
+
}
|
|
103
|
+
// 2. Tear down old live adapter background loops.
|
|
104
|
+
const old = this._adapter;
|
|
105
|
+
if (old instanceof LiveAdapter) {
|
|
106
|
+
try {
|
|
107
|
+
old.shutdown();
|
|
108
|
+
}
|
|
109
|
+
catch (err) {
|
|
110
|
+
logger.warn(TAG, `old adapter shutdown failed: ${formatError(err)}`);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
// 3. Build the new adapter.
|
|
114
|
+
const fresh = buildAdapter({
|
|
115
|
+
mode: next.mode,
|
|
116
|
+
exchange: next.exchange,
|
|
117
|
+
microLive: next.microLive,
|
|
118
|
+
simulator: this.simulator,
|
|
119
|
+
});
|
|
120
|
+
// 4. Fire async init for live adapters (non-blocking — readiness flips
|
|
121
|
+
// INIT_PENDING → READY/DEGRADED/BLOCKED on its own).
|
|
122
|
+
if (fresh instanceof LiveAdapter) {
|
|
123
|
+
fresh.initialize().catch(err => {
|
|
124
|
+
logger.error(TAG, `New adapter init failed: ${formatError(err)}`);
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
// 5. Atomically publish the new adapter.
|
|
128
|
+
// Note: `next.mode` is the operator's intent (e.g. SHADOW), which is
|
|
129
|
+
// not always the same as `fresh.mode` — PaperAdapter is used for both
|
|
130
|
+
// PAPER and SHADOW execution, and reports its mode as 'PAPER'. The
|
|
131
|
+
// runtime tracks the user-facing mode separately so tools that branch
|
|
132
|
+
// on SHADOW (e.g. risk summary) keep working.
|
|
133
|
+
this._adapter = fresh;
|
|
134
|
+
this._mode = next.mode;
|
|
135
|
+
deps.adapterDeps.adapter = fresh;
|
|
136
|
+
// 6. Start a new stop-watcher bound to the new adapter.
|
|
137
|
+
const watcher = new PositionWatcher(fresh);
|
|
138
|
+
watcher.start();
|
|
139
|
+
this._stopWatcher = watcher;
|
|
140
|
+
// 7. Paper market feed follows the mode: run it when the new adapter is
|
|
141
|
+
// paper (so marks/stops/NAV stay live), stop it in live (the live
|
|
142
|
+
// adapter sources marks from Binance directly). Idempotent both ways.
|
|
143
|
+
if (this._marketFeed) {
|
|
144
|
+
if (fresh.isLive) {
|
|
145
|
+
this._marketFeed.stop();
|
|
146
|
+
}
|
|
147
|
+
else {
|
|
148
|
+
this._marketFeed.start();
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
logger.info(TAG, `Reconnect complete: now in ${this._mode} mode (readiness=${fresh.readiness})`);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "reefclaw-paper-trading",
|
|
3
|
+
"name": "ReefClaw Paper Trading",
|
|
4
|
+
"version": "0.1.0",
|
|
5
|
+
"description": "Paper trading plugin with real Binance market data and simulated execution. No API keys required.",
|
|
6
|
+
"author": "ReefClaw",
|
|
7
|
+
"activation": {
|
|
8
|
+
"onStartup": true
|
|
9
|
+
},
|
|
10
|
+
"contracts": {
|
|
11
|
+
"tools": [
|
|
12
|
+
"fetch_ticker",
|
|
13
|
+
"fetch_ohlcv",
|
|
14
|
+
"fetch_balance",
|
|
15
|
+
"fetch_positions",
|
|
16
|
+
"fetch_open_orders",
|
|
17
|
+
"create_order",
|
|
18
|
+
"cancel_order",
|
|
19
|
+
"cancel_all_orders",
|
|
20
|
+
"close_position",
|
|
21
|
+
"modify_stop",
|
|
22
|
+
"modify_target",
|
|
23
|
+
"attach_brackets",
|
|
24
|
+
"audit_bracket_protection",
|
|
25
|
+
"record_position_reviews",
|
|
26
|
+
"get_my_recent_reviews",
|
|
27
|
+
"get_relevant_learnings",
|
|
28
|
+
"get_my_mined_patterns",
|
|
29
|
+
"get_my_proposed_learnings",
|
|
30
|
+
"propose_learning",
|
|
31
|
+
"query_review_outcomes",
|
|
32
|
+
"get_market_structure",
|
|
33
|
+
"get_crypto_metrics",
|
|
34
|
+
"get_volume_analysis",
|
|
35
|
+
"get_risk_summary",
|
|
36
|
+
"get_market_intel",
|
|
37
|
+
"get_regime",
|
|
38
|
+
"get_signals",
|
|
39
|
+
"get_analytics",
|
|
40
|
+
"get_orderbook",
|
|
41
|
+
"get_volume_profile",
|
|
42
|
+
"get_liquidation_levels",
|
|
43
|
+
"get_trade_feedback",
|
|
44
|
+
"get_trade_flow",
|
|
45
|
+
"score_setup",
|
|
46
|
+
"get_sizing",
|
|
47
|
+
"check_position_health",
|
|
48
|
+
"get_risk_scenario",
|
|
49
|
+
"get_session_review",
|
|
50
|
+
"get_backtest",
|
|
51
|
+
"get_pattern_scan",
|
|
52
|
+
"save_strategy",
|
|
53
|
+
"list_strategies",
|
|
54
|
+
"toggle_strategy",
|
|
55
|
+
"scan_pairs",
|
|
56
|
+
"get_setup_detail",
|
|
57
|
+
"query_trades",
|
|
58
|
+
"get_agent_profile",
|
|
59
|
+
"get_cvd",
|
|
60
|
+
"get_market_breadth",
|
|
61
|
+
"get_basis",
|
|
62
|
+
"get_cascade_risk",
|
|
63
|
+
"get_sentiment",
|
|
64
|
+
"get_divergences",
|
|
65
|
+
"get_liquidation_pulse",
|
|
66
|
+
"get_resting_liquidity",
|
|
67
|
+
"get_funding_context",
|
|
68
|
+
"set_exchange_credentials",
|
|
69
|
+
"set_trading_mode",
|
|
70
|
+
"test_exchange_credentials",
|
|
71
|
+
"clear_exchange_credentials",
|
|
72
|
+
"get_bracket_config",
|
|
73
|
+
"set_bracket_requirement"
|
|
74
|
+
]
|
|
75
|
+
},
|
|
76
|
+
"configSchema": {
|
|
77
|
+
"type": "object",
|
|
78
|
+
"properties": {
|
|
79
|
+
"startingBalance": {
|
|
80
|
+
"type": "number",
|
|
81
|
+
"default": 10000,
|
|
82
|
+
"description": "Starting USDT balance for paper trading"
|
|
83
|
+
},
|
|
84
|
+
"symbol": {
|
|
85
|
+
"type": "string",
|
|
86
|
+
"default": "BTC/USDT",
|
|
87
|
+
"description": "Default trading symbol"
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
},
|
|
91
|
+
"skills": [
|
|
92
|
+
"./skills"
|
|
93
|
+
]
|
|
94
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@reefclaw/openclaw-plugin",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "ReefClaw trading plugin for OpenClaw — paper trading with real Binance market data, plus the ReefClaw dashboard connector (supervised by OpenClaw, no service manager needed). Install: /plugins install npm:@reefclaw/openclaw-plugin",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "index.js",
|
|
7
|
+
"openclaw": {
|
|
8
|
+
"extensions": ["./index.js"]
|
|
9
|
+
},
|
|
10
|
+
"files": [
|
|
11
|
+
"**/*"
|
|
12
|
+
],
|
|
13
|
+
"engines": {
|
|
14
|
+
"node": ">=20"
|
|
15
|
+
},
|
|
16
|
+
"dependencies": {
|
|
17
|
+
"@reefclaw/shared": "0.1.0",
|
|
18
|
+
"ccxt": "4.5.37",
|
|
19
|
+
"json5": "2.2.3",
|
|
20
|
+
"ws": "8.19.0"
|
|
21
|
+
},
|
|
22
|
+
"scripts": {
|
|
23
|
+
"build": "node scripts/assemble.mjs"
|
|
24
|
+
},
|
|
25
|
+
"license": "MIT",
|
|
26
|
+
"homepage": "https://reefclaw.com"
|
|
27
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { IExchangeAdapter, AdapterReadiness, OrderOptions } from './exchange-adapter.js';
|
|
2
|
+
import type { ExchangeSimulator } from './simulator/exchange-simulator.js';
|
|
3
|
+
import type { CcxtOrder, CcxtBalance, CcxtPosition } from './types.js';
|
|
4
|
+
import type { PositionMetadata, CloseReason } from './simulator/types.js';
|
|
5
|
+
export declare class PaperAdapter implements IExchangeAdapter {
|
|
6
|
+
private simulator;
|
|
7
|
+
readonly mode: "PAPER";
|
|
8
|
+
readonly isLive = false;
|
|
9
|
+
readonly readiness: AdapterReadiness;
|
|
10
|
+
constructor(simulator: ExchangeSimulator);
|
|
11
|
+
/** Get the underlying simulator (for paper-only operations like ATR, orderbook, state). */
|
|
12
|
+
getSimulator(): ExchangeSimulator;
|
|
13
|
+
createOrder(symbol: string, side: 'buy' | 'sell', type: 'market' | 'limit', amount: number, price?: number, metadata?: PositionMetadata, _options?: OrderOptions): Promise<CcxtOrder>;
|
|
14
|
+
cancelOrder(orderId: string, _symbol?: string): Promise<CcxtOrder>;
|
|
15
|
+
cancelAllOrders(symbol?: string): Promise<CcxtOrder[]>;
|
|
16
|
+
closePosition(symbol: string, closeReason?: CloseReason): Promise<CcxtOrder>;
|
|
17
|
+
getBalance(): Promise<CcxtBalance>;
|
|
18
|
+
getPositions(symbol?: string): Promise<CcxtPosition[]>;
|
|
19
|
+
/** Paper has no exchange fetch to fail — position state is always known. */
|
|
20
|
+
getPositionsOrNull(symbol?: string): Promise<CcxtPosition[] | null>;
|
|
21
|
+
getOpenOrders(symbol?: string): Promise<CcxtOrder[]>;
|
|
22
|
+
fetchOrder(orderId: string, _symbol?: string): Promise<CcxtOrder | null>;
|
|
23
|
+
getLastPrice(symbol: string): Promise<number | null>;
|
|
24
|
+
}
|
package/paper-adapter.js
ADDED
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
// PaperAdapter — wraps ExchangeSimulator to implement IExchangeAdapter.
|
|
2
|
+
// No behavioral changes — just a thin async wrapper for uniform interface.
|
|
3
|
+
export class PaperAdapter {
|
|
4
|
+
simulator;
|
|
5
|
+
mode = 'PAPER';
|
|
6
|
+
isLive = false;
|
|
7
|
+
readiness = 'READY'; // Paper is always ready
|
|
8
|
+
constructor(simulator) {
|
|
9
|
+
this.simulator = simulator;
|
|
10
|
+
}
|
|
11
|
+
/** Get the underlying simulator (for paper-only operations like ATR, orderbook, state). */
|
|
12
|
+
getSimulator() {
|
|
13
|
+
return this.simulator;
|
|
14
|
+
}
|
|
15
|
+
async createOrder(symbol, side, type, amount, price, metadata, _options) {
|
|
16
|
+
// Paper mode ignores OrderOptions (clientOrderId, reduceOnly, positionSide)
|
|
17
|
+
return this.simulator.createOrder(symbol, side, type, amount, price, metadata);
|
|
18
|
+
}
|
|
19
|
+
async cancelOrder(orderId, _symbol) {
|
|
20
|
+
return this.simulator.cancelOrder(orderId);
|
|
21
|
+
}
|
|
22
|
+
async cancelAllOrders(symbol) {
|
|
23
|
+
return this.simulator.cancelAllOrders(symbol);
|
|
24
|
+
}
|
|
25
|
+
async closePosition(symbol, closeReason) {
|
|
26
|
+
return this.simulator.closePosition(symbol, closeReason);
|
|
27
|
+
}
|
|
28
|
+
async getBalance() {
|
|
29
|
+
return this.simulator.getBalance();
|
|
30
|
+
}
|
|
31
|
+
async getPositions(symbol) {
|
|
32
|
+
return this.simulator.getPositions(symbol);
|
|
33
|
+
}
|
|
34
|
+
/** Paper has no exchange fetch to fail — position state is always known. */
|
|
35
|
+
async getPositionsOrNull(symbol) {
|
|
36
|
+
return this.simulator.getPositions(symbol);
|
|
37
|
+
}
|
|
38
|
+
async getOpenOrders(symbol) {
|
|
39
|
+
return this.simulator.getOpenOrders(symbol);
|
|
40
|
+
}
|
|
41
|
+
async fetchOrder(orderId, _symbol) {
|
|
42
|
+
// Search open orders, then trade history for filled/canceled orders
|
|
43
|
+
const state = this.simulator.getState();
|
|
44
|
+
const openOrder = state.openOrders.find(o => o.id === orderId);
|
|
45
|
+
if (openOrder) {
|
|
46
|
+
return {
|
|
47
|
+
id: openOrder.id,
|
|
48
|
+
symbol: openOrder.symbol,
|
|
49
|
+
side: openOrder.side,
|
|
50
|
+
type: openOrder.type,
|
|
51
|
+
status: openOrder.status,
|
|
52
|
+
amount: openOrder.amount,
|
|
53
|
+
filled: openOrder.filled,
|
|
54
|
+
remaining: openOrder.amount - openOrder.filled,
|
|
55
|
+
average: openOrder.average,
|
|
56
|
+
price: openOrder.price,
|
|
57
|
+
cost: openOrder.cost,
|
|
58
|
+
fee: openOrder.fee,
|
|
59
|
+
timestamp: new Date(openOrder.createdAt).getTime(),
|
|
60
|
+
datetime: openOrder.createdAt,
|
|
61
|
+
timeInForce: 'GTC',
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
// Check trade history for filled orders
|
|
65
|
+
const trade = state.tradeHistory.find(t => t.orderId === orderId);
|
|
66
|
+
if (trade) {
|
|
67
|
+
return {
|
|
68
|
+
id: trade.orderId,
|
|
69
|
+
symbol: trade.symbol,
|
|
70
|
+
side: trade.side,
|
|
71
|
+
type: 'market', // Trades don't store order type; market is the common close path
|
|
72
|
+
status: 'closed',
|
|
73
|
+
amount: trade.quantity,
|
|
74
|
+
filled: trade.quantity,
|
|
75
|
+
remaining: 0,
|
|
76
|
+
average: trade.exitPrice,
|
|
77
|
+
price: trade.exitPrice,
|
|
78
|
+
cost: trade.exitPrice * trade.quantity,
|
|
79
|
+
fee: { cost: trade.fee, currency: 'USDT' },
|
|
80
|
+
timestamp: new Date(trade.closedAt).getTime(),
|
|
81
|
+
datetime: trade.closedAt,
|
|
82
|
+
timeInForce: 'GTC',
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
return null;
|
|
86
|
+
}
|
|
87
|
+
async getLastPrice(symbol) {
|
|
88
|
+
const ticker = this.simulator.getLastTicker(symbol);
|
|
89
|
+
return ticker?.last ?? null;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import type { SimulatorState } from '../simulator/types.js';
|
|
2
|
+
export declare class StateManager {
|
|
3
|
+
private readonly statePath;
|
|
4
|
+
private saveTimer;
|
|
5
|
+
private pendingState;
|
|
6
|
+
constructor(pluginId: string, baseDir?: string);
|
|
7
|
+
getStatePath(): string;
|
|
8
|
+
/** Quarantine a state file that exists but won't parse, instead of letting
|
|
9
|
+
* the caller silently overwrite it with a fresh default (which would wipe
|
|
10
|
+
* the positions/orders/realized-PnL ledger). The corrupt bytes are renamed
|
|
11
|
+
* to `<state>.corrupt-<ts>` so an operator can recover them. Returns true if
|
|
12
|
+
* a corrupt file was quarantined. */
|
|
13
|
+
private quarantineCorruptSync;
|
|
14
|
+
/** Load state from disk. Returns null when the file is MISSING (fresh start)
|
|
15
|
+
* OR when it exists-but-is-unreadable (in which case the corrupt bytes are
|
|
16
|
+
* quarantined first — never silently discarded). The two cases are
|
|
17
|
+
* distinguished in the log; both yield null so the caller seeds a default,
|
|
18
|
+
* but a corrupt file is preserved as `<state>.corrupt-<ts>`. */
|
|
19
|
+
load(): Promise<SimulatorState | null>;
|
|
20
|
+
/** Save state atomically (write to a UNIQUE temp, rename to final). A unique
|
|
21
|
+
* temp name (pid + uuid) is essential under the two-process model: both the
|
|
22
|
+
* agent and gateway processes wire stateChanged→save, and a shared
|
|
23
|
+
* `<state>.tmp` let their concurrent writes interleave into a torn file that
|
|
24
|
+
* then parses as garbage on next boot. With a per-write temp, the worst case
|
|
25
|
+
* degrades to a harmless last-writer-wins rename of two COMPLETE files. */
|
|
26
|
+
save(state: SimulatorState): Promise<void>;
|
|
27
|
+
/**
|
|
28
|
+
* Debounced save — coalesces rapid mutations into one write per second.
|
|
29
|
+
* Call this on every state change; it will save at most once per second.
|
|
30
|
+
*/
|
|
31
|
+
debouncedSave(state: SimulatorState): void;
|
|
32
|
+
/** Flush any pending debounced save immediately. */
|
|
33
|
+
flush(): Promise<void>;
|
|
34
|
+
/** Load state or create default if not found. */
|
|
35
|
+
loadOrDefault(startingBalance: number, quoteCurrency: string): Promise<SimulatorState>;
|
|
36
|
+
/** Synchronous load — for use in synchronous plugin register(). Same
|
|
37
|
+
* missing-vs-corrupt distinction as load(): a corrupt file is quarantined
|
|
38
|
+
* (preserved), never silently overwritten by the default the caller seeds. */
|
|
39
|
+
loadSync(): SimulatorState | null;
|
|
40
|
+
/** Synchronous load-or-create-default — for use in synchronous plugin register(). */
|
|
41
|
+
loadOrDefaultSync(startingBalance: number, quoteCurrency: string): SimulatorState;
|
|
42
|
+
}
|