@raintree-technology/perps 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/CHANGELOG.md +33 -0
- package/LICENSE +21 -0
- package/README.md +175 -0
- package/dist/adapters/aevo.d.ts +64 -0
- package/dist/adapters/aevo.js +899 -0
- package/dist/adapters/certification.d.ts +33 -0
- package/dist/adapters/certification.js +99 -0
- package/dist/adapters/decibel/order-manager.d.ts +45 -0
- package/dist/adapters/decibel/order-manager.js +140 -0
- package/dist/adapters/decibel/rest-client.d.ts +176 -0
- package/dist/adapters/decibel/rest-client.js +155 -0
- package/dist/adapters/decibel/ws-feed.d.ts +28 -0
- package/dist/adapters/decibel/ws-feed.js +166 -0
- package/dist/adapters/decibel.d.ts +108 -0
- package/dist/adapters/decibel.js +1377 -0
- package/dist/adapters/hyperliquid.d.ts +63 -0
- package/dist/adapters/hyperliquid.js +797 -0
- package/dist/adapters/index.d.ts +11 -0
- package/dist/adapters/index.js +12 -0
- package/dist/adapters/interface.d.ts +310 -0
- package/dist/adapters/interface.js +15 -0
- package/dist/adapters/orderly.d.ts +70 -0
- package/dist/adapters/orderly.js +936 -0
- package/dist/adapters/paradex.d.ts +69 -0
- package/dist/adapters/paradex.js +862 -0
- package/dist/adapters/utils.d.ts +17 -0
- package/dist/adapters/utils.js +122 -0
- package/dist/cli/command-metadata.d.ts +2 -0
- package/dist/cli/command-metadata.js +44 -0
- package/dist/cli/context.d.ts +14 -0
- package/dist/cli/context.js +59 -0
- package/dist/cli/experience.d.ts +48 -0
- package/dist/cli/experience.js +243 -0
- package/dist/cli/ink/app/AppShell.d.ts +12 -0
- package/dist/cli/ink/app/AppShell.js +32 -0
- package/dist/cli/ink/app/MetricStrip.d.ts +6 -0
- package/dist/cli/ink/app/MetricStrip.js +14 -0
- package/dist/cli/ink/app/Panel.d.ts +9 -0
- package/dist/cli/ink/app/Panel.js +7 -0
- package/dist/cli/ink/app/ascii.d.ts +2 -0
- package/dist/cli/ink/app/ascii.js +46 -0
- package/dist/cli/ink/app/index.d.ts +5 -0
- package/dist/cli/ink/app/index.js +4 -0
- package/dist/cli/ink/app/types.d.ts +15 -0
- package/dist/cli/ink/app/types.js +1 -0
- package/dist/cli/ink/components/PnL.d.ts +12 -0
- package/dist/cli/ink/components/PnL.js +23 -0
- package/dist/cli/ink/components/Spinner.d.ts +13 -0
- package/dist/cli/ink/components/Spinner.js +13 -0
- package/dist/cli/ink/components/Table.d.ts +14 -0
- package/dist/cli/ink/components/Table.js +42 -0
- package/dist/cli/ink/components/WatchHeader.d.ts +10 -0
- package/dist/cli/ink/components/WatchHeader.js +18 -0
- package/dist/cli/ink/components/index.d.ts +4 -0
- package/dist/cli/ink/components/index.js +4 -0
- package/dist/cli/ink/index.d.ts +4 -0
- package/dist/cli/ink/index.js +4 -0
- package/dist/cli/ink/render.d.ts +12 -0
- package/dist/cli/ink/render.js +21 -0
- package/dist/cli/ink/theme.d.ts +29 -0
- package/dist/cli/ink/theme.js +40 -0
- package/dist/cli/network-defaults.d.ts +10 -0
- package/dist/cli/network-defaults.js +35 -0
- package/dist/cli/output.d.ts +11 -0
- package/dist/cli/output.js +115 -0
- package/dist/cli/program.d.ts +18 -0
- package/dist/cli/program.js +164 -0
- package/dist/cli/watch.d.ts +19 -0
- package/dist/cli/watch.js +35 -0
- package/dist/client/index.d.ts +55 -0
- package/dist/client/index.js +157 -0
- package/dist/commands/account/add.d.ts +2 -0
- package/dist/commands/account/add.js +510 -0
- package/dist/commands/account/balances-simple.d.ts +5 -0
- package/dist/commands/account/balances-simple.js +63 -0
- package/dist/commands/account/index.d.ts +2 -0
- package/dist/commands/account/index.js +17 -0
- package/dist/commands/account/ls.d.ts +2 -0
- package/dist/commands/account/ls.js +95 -0
- package/dist/commands/account/positions-simple.d.ts +5 -0
- package/dist/commands/account/positions-simple.js +77 -0
- package/dist/commands/account/remove.d.ts +2 -0
- package/dist/commands/account/remove.js +47 -0
- package/dist/commands/account/set-default.d.ts +2 -0
- package/dist/commands/account/set-default.js +47 -0
- package/dist/commands/agent/index.d.ts +2 -0
- package/dist/commands/agent/index.js +126 -0
- package/dist/commands/arb/alert.d.ts +6 -0
- package/dist/commands/arb/alert.js +88 -0
- package/dist/commands/arb/basis-execute.d.ts +6 -0
- package/dist/commands/arb/basis-execute.js +332 -0
- package/dist/commands/arb/basis.d.ts +6 -0
- package/dist/commands/arb/basis.js +181 -0
- package/dist/commands/arb/compare.d.ts +6 -0
- package/dist/commands/arb/compare.js +216 -0
- package/dist/commands/arb/execute.d.ts +6 -0
- package/dist/commands/arb/execute.js +467 -0
- package/dist/commands/arb/funding.d.ts +6 -0
- package/dist/commands/arb/funding.js +201 -0
- package/dist/commands/arb/history.d.ts +6 -0
- package/dist/commands/arb/history.js +153 -0
- package/dist/commands/arb/index.d.ts +6 -0
- package/dist/commands/arb/index.js +29 -0
- package/dist/commands/arb/positions.d.ts +6 -0
- package/dist/commands/arb/positions.js +158 -0
- package/dist/commands/arb/spread.d.ts +6 -0
- package/dist/commands/arb/spread.js +253 -0
- package/dist/commands/arb/track.d.ts +6 -0
- package/dist/commands/arb/track.js +259 -0
- package/dist/commands/asset/book-simple.d.ts +5 -0
- package/dist/commands/asset/book-simple.js +77 -0
- package/dist/commands/asset/index.d.ts +2 -0
- package/dist/commands/asset/index.js +5 -0
- package/dist/commands/completion.d.ts +2 -0
- package/dist/commands/completion.js +161 -0
- package/dist/commands/config/index.d.ts +5 -0
- package/dist/commands/config/index.js +109 -0
- package/dist/commands/data/index.d.ts +31 -0
- package/dist/commands/data/index.js +1466 -0
- package/dist/commands/doctor.d.ts +2 -0
- package/dist/commands/doctor.js +201 -0
- package/dist/commands/exchange/index.d.ts +2 -0
- package/dist/commands/exchange/index.js +107 -0
- package/dist/commands/index.d.ts +2 -0
- package/dist/commands/index.js +48 -0
- package/dist/commands/markets/index.d.ts +2 -0
- package/dist/commands/markets/index.js +5 -0
- package/dist/commands/markets/ls-simple.d.ts +7 -0
- package/dist/commands/markets/ls-simple.js +277 -0
- package/dist/commands/operator/index.d.ts +2 -0
- package/dist/commands/operator/index.js +146 -0
- package/dist/commands/order/cancel-simple.d.ts +5 -0
- package/dist/commands/order/cancel-simple.js +104 -0
- package/dist/commands/order/index.d.ts +2 -0
- package/dist/commands/order/index.js +13 -0
- package/dist/commands/order/limit-simple.d.ts +5 -0
- package/dist/commands/order/limit-simple.js +195 -0
- package/dist/commands/order/market-simple.d.ts +5 -0
- package/dist/commands/order/market-simple.js +190 -0
- package/dist/commands/order/shared.d.ts +17 -0
- package/dist/commands/order/shared.js +51 -0
- package/dist/commands/order/trigger-simple.d.ts +5 -0
- package/dist/commands/order/trigger-simple.js +246 -0
- package/dist/commands/referral/index.d.ts +2 -0
- package/dist/commands/referral/index.js +7 -0
- package/dist/commands/referral/set.d.ts +2 -0
- package/dist/commands/referral/set.js +26 -0
- package/dist/commands/referral/status.d.ts +2 -0
- package/dist/commands/referral/status.js +31 -0
- package/dist/commands/replay/index.d.ts +2 -0
- package/dist/commands/replay/index.js +152 -0
- package/dist/commands/risk/analytics.d.ts +2 -0
- package/dist/commands/risk/analytics.js +64 -0
- package/dist/commands/risk/audit.d.ts +2 -0
- package/dist/commands/risk/audit.js +52 -0
- package/dist/commands/risk/index.d.ts +2 -0
- package/dist/commands/risk/index.js +9 -0
- package/dist/commands/risk/rules.d.ts +2 -0
- package/dist/commands/risk/rules.js +102 -0
- package/dist/commands/server.d.ts +2 -0
- package/dist/commands/server.js +208 -0
- package/dist/commands/setup/index.d.ts +2 -0
- package/dist/commands/setup/index.js +478 -0
- package/dist/commands/signal/index.d.ts +2 -0
- package/dist/commands/signal/index.js +129 -0
- package/dist/commands/state/index.d.ts +2 -0
- package/dist/commands/state/index.js +5 -0
- package/dist/commands/state/show.d.ts +2 -0
- package/dist/commands/state/show.js +105 -0
- package/dist/commands/strategy/index.d.ts +4 -0
- package/dist/commands/strategy/index.js +73 -0
- package/dist/commands/traces/index.d.ts +2 -0
- package/dist/commands/traces/index.js +76 -0
- package/dist/commands/ui/demo.d.ts +9 -0
- package/dist/commands/ui/demo.js +195 -0
- package/dist/commands/ui/index.d.ts +2 -0
- package/dist/commands/ui/index.js +7 -0
- package/dist/commands/ui/terminal.d.ts +2 -0
- package/dist/commands/ui/terminal.js +255 -0
- package/dist/commands/upgrade.d.ts +2 -0
- package/dist/commands/upgrade.js +98 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +4 -0
- package/dist/lib/agent/audit.d.ts +12 -0
- package/dist/lib/agent/audit.js +13 -0
- package/dist/lib/agent/gateway.d.ts +13 -0
- package/dist/lib/agent/gateway.js +598 -0
- package/dist/lib/agent/metrics.d.ts +33 -0
- package/dist/lib/agent/metrics.js +175 -0
- package/dist/lib/agent/signature.d.ts +8 -0
- package/dist/lib/agent/signature.js +28 -0
- package/dist/lib/agent/tools.d.ts +28 -0
- package/dist/lib/agent/tools.js +453 -0
- package/dist/lib/agent/x402.d.ts +23 -0
- package/dist/lib/agent/x402.js +62 -0
- package/dist/lib/api-wallet.d.ts +69 -0
- package/dist/lib/api-wallet.js +101 -0
- package/dist/lib/balance-watcher.d.ts +25 -0
- package/dist/lib/balance-watcher.js +83 -0
- package/dist/lib/book-watcher.d.ts +25 -0
- package/dist/lib/book-watcher.js +48 -0
- package/dist/lib/config.d.ts +88 -0
- package/dist/lib/config.js +427 -0
- package/dist/lib/constants.d.ts +50 -0
- package/dist/lib/constants.js +84 -0
- package/dist/lib/contracts.d.ts +7 -0
- package/dist/lib/contracts.js +8 -0
- package/dist/lib/credential-vault.d.ts +22 -0
- package/dist/lib/credential-vault.js +109 -0
- package/dist/lib/db/accounts.d.ts +83 -0
- package/dist/lib/db/accounts.js +203 -0
- package/dist/lib/db/funding-history.d.ts +69 -0
- package/dist/lib/db/funding-history.js +183 -0
- package/dist/lib/db/index.d.ts +11 -0
- package/dist/lib/db/index.js +272 -0
- package/dist/lib/events/bus.d.ts +10 -0
- package/dist/lib/events/bus.js +17 -0
- package/dist/lib/events/types.d.ts +51 -0
- package/dist/lib/events/types.js +1 -0
- package/dist/lib/exchange.d.ts +30 -0
- package/dist/lib/exchange.js +84 -0
- package/dist/lib/execution/journal.d.ts +25 -0
- package/dist/lib/execution/journal.js +158 -0
- package/dist/lib/execution/safety.d.ts +34 -0
- package/dist/lib/execution/safety.js +197 -0
- package/dist/lib/exit-codes.d.ts +18 -0
- package/dist/lib/exit-codes.js +60 -0
- package/dist/lib/fetch.d.ts +18 -0
- package/dist/lib/fetch.js +66 -0
- package/dist/lib/fs-security.d.ts +10 -0
- package/dist/lib/fs-security.js +26 -0
- package/dist/lib/funding-tracker.d.ts +40 -0
- package/dist/lib/funding-tracker.js +118 -0
- package/dist/lib/logger.d.ts +27 -0
- package/dist/lib/logger.js +82 -0
- package/dist/lib/network-model.d.ts +13 -0
- package/dist/lib/network-model.js +30 -0
- package/dist/lib/onboarding.d.ts +133 -0
- package/dist/lib/onboarding.js +1459 -0
- package/dist/lib/operator-state.d.ts +25 -0
- package/dist/lib/operator-state.js +82 -0
- package/dist/lib/orders-watcher.d.ts +24 -0
- package/dist/lib/orders-watcher.js +74 -0
- package/dist/lib/paths.d.ts +20 -0
- package/dist/lib/paths.js +23 -0
- package/dist/lib/portfolio-watcher.d.ts +33 -0
- package/dist/lib/portfolio-watcher.js +95 -0
- package/dist/lib/position-watcher.d.ts +16 -0
- package/dist/lib/position-watcher.js +44 -0
- package/dist/lib/price-watcher.d.ts +15 -0
- package/dist/lib/price-watcher.js +84 -0
- package/dist/lib/prompts.d.ts +32 -0
- package/dist/lib/prompts.js +105 -0
- package/dist/lib/rate-limit.d.ts +32 -0
- package/dist/lib/rate-limit.js +88 -0
- package/dist/lib/risk/analytics.d.ts +39 -0
- package/dist/lib/risk/analytics.js +98 -0
- package/dist/lib/risk/drawdown.d.ts +18 -0
- package/dist/lib/risk/drawdown.js +49 -0
- package/dist/lib/risk/evaluation-log.d.ts +29 -0
- package/dist/lib/risk/evaluation-log.js +61 -0
- package/dist/lib/risk/index.d.ts +4 -0
- package/dist/lib/risk/index.js +4 -0
- package/dist/lib/risk/limits.d.ts +23 -0
- package/dist/lib/risk/limits.js +27 -0
- package/dist/lib/risk/manager.d.ts +32 -0
- package/dist/lib/risk/manager.js +85 -0
- package/dist/lib/risk/policy-middleware.d.ts +33 -0
- package/dist/lib/risk/policy-middleware.js +267 -0
- package/dist/lib/risk/position-sizer.d.ts +9 -0
- package/dist/lib/risk/position-sizer.js +14 -0
- package/dist/lib/risk/rules-store.d.ts +16 -0
- package/dist/lib/risk/rules-store.js +47 -0
- package/dist/lib/schema.d.ts +254 -0
- package/dist/lib/schema.js +199 -0
- package/dist/lib/secrets.d.ts +3 -0
- package/dist/lib/secrets.js +62 -0
- package/dist/lib/settings.d.ts +24 -0
- package/dist/lib/settings.js +86 -0
- package/dist/lib/signals.d.ts +73 -0
- package/dist/lib/signals.js +136 -0
- package/dist/lib/stable-stringify.d.ts +6 -0
- package/dist/lib/stable-stringify.js +17 -0
- package/dist/lib/state-context.d.ts +44 -0
- package/dist/lib/state-context.js +133 -0
- package/dist/lib/strategy/basis-trade.d.ts +2 -0
- package/dist/lib/strategy/basis-trade.js +24 -0
- package/dist/lib/strategy/funding-arb.d.ts +2 -0
- package/dist/lib/strategy/funding-arb.js +23 -0
- package/dist/lib/strategy/interface.d.ts +23 -0
- package/dist/lib/strategy/interface.js +1 -0
- package/dist/lib/strategy/registry.d.ts +4 -0
- package/dist/lib/strategy/registry.js +10 -0
- package/dist/lib/telemetry.d.ts +25 -0
- package/dist/lib/telemetry.js +101 -0
- package/dist/lib/trace-queries.d.ts +20 -0
- package/dist/lib/trace-queries.js +133 -0
- package/dist/lib/trace.d.ts +1 -0
- package/dist/lib/trace.js +4 -0
- package/dist/lib/trade-reputation.d.ts +6 -0
- package/dist/lib/trade-reputation.js +99 -0
- package/dist/lib/ui-tokens.d.ts +21 -0
- package/dist/lib/ui-tokens.js +26 -0
- package/dist/lib/validate.d.ts +39 -0
- package/dist/lib/validate.js +108 -0
- package/dist/lib/validation.d.ts +9 -0
- package/dist/lib/validation.js +64 -0
- package/dist/server/cache.d.ts +38 -0
- package/dist/server/cache.js +56 -0
- package/dist/server/index.d.ts +2 -0
- package/dist/server/index.js +89 -0
- package/dist/server/ipc.d.ts +18 -0
- package/dist/server/ipc.js +159 -0
- package/dist/server/subscriptions.d.ts +18 -0
- package/dist/server/subscriptions.js +114 -0
- package/package.json +124 -0
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
import { getOutputOptions } from "../../cli/program.js";
|
|
2
|
+
import { output, outputError, outputSuccess } from "../../cli/output.js";
|
|
3
|
+
import { withJsonContract } from "../../lib/contracts.js";
|
|
4
|
+
import { readOperatorState, setKillSwitch } from "../../lib/operator-state.js";
|
|
5
|
+
import { getTelemetrySnapshot, resetTelemetry, summarizeFailureRates } from "../../lib/telemetry.js";
|
|
6
|
+
export function registerOperatorCommands(program) {
|
|
7
|
+
const operator = program
|
|
8
|
+
.command("operator")
|
|
9
|
+
.alias("bot")
|
|
10
|
+
.description("Operator controls, heartbeat, and kill-switch");
|
|
11
|
+
operator
|
|
12
|
+
.command("status")
|
|
13
|
+
.description("Show latest heartbeat and kill-switch status")
|
|
14
|
+
.action(function () {
|
|
15
|
+
const outputOpts = getOutputOptions(this);
|
|
16
|
+
const state = readOperatorState();
|
|
17
|
+
const heartbeat = state.heartbeat;
|
|
18
|
+
const halted = state.killSwitch.enabled || (heartbeat?.halted ?? false);
|
|
19
|
+
const haltReason = state.killSwitch.enabled
|
|
20
|
+
? state.killSwitch.reason ?? "Manual kill switch enabled"
|
|
21
|
+
: heartbeat?.haltReason;
|
|
22
|
+
const status = {
|
|
23
|
+
halted,
|
|
24
|
+
haltReason,
|
|
25
|
+
killSwitch: state.killSwitch,
|
|
26
|
+
heartbeat: heartbeat ?? null,
|
|
27
|
+
};
|
|
28
|
+
if (outputOpts.json) {
|
|
29
|
+
output(status, outputOpts);
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
console.log("\nOperator Status:\n");
|
|
33
|
+
console.log(` Halted: ${halted ? "YES" : "NO"}`);
|
|
34
|
+
if (haltReason) {
|
|
35
|
+
console.log(` Halt reason: ${haltReason}`);
|
|
36
|
+
}
|
|
37
|
+
console.log(` Kill switch: ${state.killSwitch.enabled ? "ON" : "OFF"}`);
|
|
38
|
+
if (state.killSwitch.updatedAt > 0) {
|
|
39
|
+
console.log(` Updated: ${new Date(state.killSwitch.updatedAt).toISOString()}`);
|
|
40
|
+
}
|
|
41
|
+
console.log("");
|
|
42
|
+
if (!heartbeat) {
|
|
43
|
+
console.log(" No heartbeat recorded yet.");
|
|
44
|
+
console.log(" Run an order/arb execution command to emit a heartbeat snapshot.\n");
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
console.log(" Latest Heartbeat:");
|
|
48
|
+
console.log(` Exchange: ${heartbeat.exchange}`);
|
|
49
|
+
if (heartbeat.market)
|
|
50
|
+
console.log(` Market: ${heartbeat.market}`);
|
|
51
|
+
console.log(` Equity: $${heartbeat.equity.toFixed(2)}`);
|
|
52
|
+
console.log(` Exposure: $${heartbeat.exposureUsd.toFixed(2)}`);
|
|
53
|
+
console.log(` Drawdown: ${heartbeat.drawdownPct.toFixed(2)}%`);
|
|
54
|
+
console.log(` Peak: $${heartbeat.peakEquity.toFixed(2)}`);
|
|
55
|
+
console.log(` Halted: ${heartbeat.halted ? "YES" : "NO"}`);
|
|
56
|
+
if (heartbeat.haltReason) {
|
|
57
|
+
console.log(` Reason: ${heartbeat.haltReason}`);
|
|
58
|
+
}
|
|
59
|
+
console.log(` Updated: ${new Date(heartbeat.updatedAt).toISOString()} (${formatAge(Date.now() - heartbeat.updatedAt)} ago)`);
|
|
60
|
+
console.log("");
|
|
61
|
+
});
|
|
62
|
+
operator
|
|
63
|
+
.command("halt [reason]")
|
|
64
|
+
.description("Enable manual kill switch")
|
|
65
|
+
.action(function (reason) {
|
|
66
|
+
const outputOpts = getOutputOptions(this);
|
|
67
|
+
const state = setKillSwitch(true, reason);
|
|
68
|
+
if (outputOpts.json) {
|
|
69
|
+
output({ success: true, killSwitch: state.killSwitch }, outputOpts);
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
outputSuccess("Kill switch enabled");
|
|
73
|
+
if (state.killSwitch.reason) {
|
|
74
|
+
console.log(` Reason: ${state.killSwitch.reason}`);
|
|
75
|
+
}
|
|
76
|
+
console.log("");
|
|
77
|
+
});
|
|
78
|
+
operator
|
|
79
|
+
.command("resume")
|
|
80
|
+
.description("Disable manual kill switch")
|
|
81
|
+
.action(function () {
|
|
82
|
+
const outputOpts = getOutputOptions(this);
|
|
83
|
+
const state = setKillSwitch(false);
|
|
84
|
+
if (outputOpts.json) {
|
|
85
|
+
output({ success: true, killSwitch: state.killSwitch }, outputOpts);
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
outputSuccess("Kill switch disabled");
|
|
89
|
+
if (state.heartbeat?.halted) {
|
|
90
|
+
outputError("Circuit breaker is still halted by drawdown. Use safer sizing or reset state if intentional.");
|
|
91
|
+
}
|
|
92
|
+
console.log("");
|
|
93
|
+
});
|
|
94
|
+
operator
|
|
95
|
+
.command("telemetry")
|
|
96
|
+
.description("Show auth/funding reliability telemetry")
|
|
97
|
+
.option("--reset", "Reset telemetry counters after printing")
|
|
98
|
+
.action(function () {
|
|
99
|
+
const outputOpts = getOutputOptions(this);
|
|
100
|
+
const opts = this.opts();
|
|
101
|
+
const snapshot = getTelemetrySnapshot();
|
|
102
|
+
const rates = summarizeFailureRates(snapshot);
|
|
103
|
+
if (outputOpts.json) {
|
|
104
|
+
output(withJsonContract("operator.telemetry.snapshot", {
|
|
105
|
+
snapshot,
|
|
106
|
+
rates,
|
|
107
|
+
}), outputOpts);
|
|
108
|
+
}
|
|
109
|
+
else {
|
|
110
|
+
console.log("\nTelemetry:\n");
|
|
111
|
+
console.log(` Updated: ${new Date(snapshot.updatedAt).toISOString()}`);
|
|
112
|
+
console.log(` Counters: ${Object.keys(snapshot.counters).length}`);
|
|
113
|
+
if (rates.length === 0) {
|
|
114
|
+
console.log(" No telemetry events recorded yet.\n");
|
|
115
|
+
}
|
|
116
|
+
else {
|
|
117
|
+
console.log("");
|
|
118
|
+
for (const row of rates) {
|
|
119
|
+
console.log(` ${row.metric}: total=${row.total} failRate=${(row.failureRate * 100).toFixed(1)}% successRate=${(row.successRate * 100).toFixed(1)}%`);
|
|
120
|
+
}
|
|
121
|
+
console.log("");
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
if (opts.reset) {
|
|
125
|
+
resetTelemetry();
|
|
126
|
+
if (!outputOpts.json) {
|
|
127
|
+
outputSuccess("Telemetry counters reset");
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
function formatAge(ms) {
|
|
133
|
+
if (ms < 1_000)
|
|
134
|
+
return `${ms}ms`;
|
|
135
|
+
const seconds = Math.floor(ms / 1_000);
|
|
136
|
+
if (seconds < 60)
|
|
137
|
+
return `${seconds}s`;
|
|
138
|
+
const minutes = Math.floor(seconds / 60);
|
|
139
|
+
if (minutes < 60)
|
|
140
|
+
return `${minutes}m ${seconds % 60}s`;
|
|
141
|
+
const hours = Math.floor(minutes / 60);
|
|
142
|
+
if (hours < 24)
|
|
143
|
+
return `${hours}h ${minutes % 60}m`;
|
|
144
|
+
const days = Math.floor(hours / 24);
|
|
145
|
+
return `${days}d ${hours % 24}h`;
|
|
146
|
+
}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Simple cancel order command using the adapter interface
|
|
3
|
+
*/
|
|
4
|
+
import { getContext, getOutputOptions, getSelectedExchange } from "../../cli/program.js";
|
|
5
|
+
import { output, outputError, outputSuccess } from "../../cli/output.js";
|
|
6
|
+
import { getExchangeAdapter } from "../../lib/exchange.js";
|
|
7
|
+
import { getExchangeCredentials } from "../../lib/config.js";
|
|
8
|
+
export function registerCancelOrderSimpleCommand(order) {
|
|
9
|
+
order
|
|
10
|
+
.command("cancel <orderId> <symbol>")
|
|
11
|
+
.description("Cancel an order by ID")
|
|
12
|
+
.action(async function (orderId, symbol) {
|
|
13
|
+
const ctx = getContext(this);
|
|
14
|
+
const outputOpts = getOutputOptions(this);
|
|
15
|
+
const exchangeId = getSelectedExchange(this);
|
|
16
|
+
let connected = false;
|
|
17
|
+
try {
|
|
18
|
+
const adapter = getExchangeAdapter();
|
|
19
|
+
const credentials = getExchangeCredentials(ctx.config, exchangeId, {
|
|
20
|
+
requireTrading: true,
|
|
21
|
+
});
|
|
22
|
+
await adapter.connect({
|
|
23
|
+
testnet: ctx.config.testnet,
|
|
24
|
+
rpcUrl: credentials.fullnodeUrl,
|
|
25
|
+
wsUrl: credentials.wsUrl,
|
|
26
|
+
credentials,
|
|
27
|
+
});
|
|
28
|
+
connected = true;
|
|
29
|
+
// Normalize market
|
|
30
|
+
let market = symbol.toUpperCase();
|
|
31
|
+
if (!market.includes("-")) {
|
|
32
|
+
market = `${market}-PERP`;
|
|
33
|
+
}
|
|
34
|
+
const success = await adapter.cancelOrder({
|
|
35
|
+
orderId,
|
|
36
|
+
market,
|
|
37
|
+
});
|
|
38
|
+
if (outputOpts.json) {
|
|
39
|
+
output({ success, orderId, market }, outputOpts);
|
|
40
|
+
}
|
|
41
|
+
else {
|
|
42
|
+
if (success) {
|
|
43
|
+
outputSuccess(`Order ${orderId} cancelled`);
|
|
44
|
+
}
|
|
45
|
+
else {
|
|
46
|
+
outputError(`Failed to cancel order ${orderId}`);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
catch (err) {
|
|
51
|
+
outputError(err instanceof Error ? err.message : String(err));
|
|
52
|
+
process.exit(1);
|
|
53
|
+
}
|
|
54
|
+
finally {
|
|
55
|
+
if (connected)
|
|
56
|
+
await getExchangeAdapter().disconnect().catch(() => undefined);
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
// Cancel all orders
|
|
60
|
+
order
|
|
61
|
+
.command("cancel-all [symbol]")
|
|
62
|
+
.description("Cancel all open orders (optionally for a specific market)")
|
|
63
|
+
.action(async function (symbol) {
|
|
64
|
+
const ctx = getContext(this);
|
|
65
|
+
const outputOpts = getOutputOptions(this);
|
|
66
|
+
const exchangeId = getSelectedExchange(this);
|
|
67
|
+
let connected = false;
|
|
68
|
+
try {
|
|
69
|
+
const adapter = getExchangeAdapter();
|
|
70
|
+
const credentials = getExchangeCredentials(ctx.config, exchangeId, {
|
|
71
|
+
requireTrading: true,
|
|
72
|
+
});
|
|
73
|
+
await adapter.connect({
|
|
74
|
+
testnet: ctx.config.testnet,
|
|
75
|
+
rpcUrl: credentials.fullnodeUrl,
|
|
76
|
+
wsUrl: credentials.wsUrl,
|
|
77
|
+
credentials,
|
|
78
|
+
});
|
|
79
|
+
connected = true;
|
|
80
|
+
let market;
|
|
81
|
+
if (symbol) {
|
|
82
|
+
market = symbol.toUpperCase();
|
|
83
|
+
if (!market.includes("-")) {
|
|
84
|
+
market = `${market}-PERP`;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
const count = await adapter.cancelAllOrders(market);
|
|
88
|
+
if (outputOpts.json) {
|
|
89
|
+
output({ cancelled: count, market: market ?? "all" }, outputOpts);
|
|
90
|
+
}
|
|
91
|
+
else {
|
|
92
|
+
outputSuccess(`Cancelled ${count} orders${market ? ` for ${market}` : ""}`);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
catch (err) {
|
|
96
|
+
outputError(err instanceof Error ? err.message : String(err));
|
|
97
|
+
process.exit(1);
|
|
98
|
+
}
|
|
99
|
+
finally {
|
|
100
|
+
if (connected)
|
|
101
|
+
await getExchangeAdapter().disconnect().catch(() => undefined);
|
|
102
|
+
}
|
|
103
|
+
});
|
|
104
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { registerLimitOrderSimpleCommand } from "./limit-simple.js";
|
|
2
|
+
import { registerMarketOrderSimpleCommand } from "./market-simple.js";
|
|
3
|
+
import { registerCancelOrderSimpleCommand } from "./cancel-simple.js";
|
|
4
|
+
import { registerTriggerOrderSimpleCommands } from "./trigger-simple.js";
|
|
5
|
+
export function registerOrderCommands(program) {
|
|
6
|
+
const order = program
|
|
7
|
+
.command("order")
|
|
8
|
+
.description("Order management and trading (requires authentication)");
|
|
9
|
+
registerLimitOrderSimpleCommand(order);
|
|
10
|
+
registerMarketOrderSimpleCommand(order);
|
|
11
|
+
registerTriggerOrderSimpleCommands(order);
|
|
12
|
+
registerCancelOrderSimpleCommand(order);
|
|
13
|
+
}
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Simple limit order command using the adapter interface
|
|
3
|
+
*/
|
|
4
|
+
import { getContext, getOutputOptions, getSelectedExchange } from "../../cli/program.js";
|
|
5
|
+
import { output, outputError, outputSuccess } from "../../cli/output.js";
|
|
6
|
+
import { getExchangeAdapter } from "../../lib/exchange.js";
|
|
7
|
+
import { getExchangeCredentials } from "../../lib/config.js";
|
|
8
|
+
import { RiskPolicyMiddleware } from "../../lib/risk/policy-middleware.js";
|
|
9
|
+
import { executeOrderWithSafety } from "../../lib/execution/safety.js";
|
|
10
|
+
import { runWithExecutionJournal } from "../../lib/execution/journal.js";
|
|
11
|
+
import { normalizeMarket, normalizeSide } from "./shared.js";
|
|
12
|
+
import { withJsonContract } from "../../lib/contracts.js";
|
|
13
|
+
import { inferExitCode } from "../../lib/exit-codes.js";
|
|
14
|
+
export function registerLimitOrderSimpleCommand(order) {
|
|
15
|
+
order
|
|
16
|
+
.command("limit <side> <symbol> <size> <price>")
|
|
17
|
+
.description("Place a limit order (side: buy/long or sell/short)")
|
|
18
|
+
.option("--reduce-only", "Reduce-only order")
|
|
19
|
+
.option("--post-only", "Post-only order (maker only)")
|
|
20
|
+
.option("--confidence <0-1>", "Signal confidence for risk policy (default: 1.0)")
|
|
21
|
+
.option("--tp <pct>", "Attach take-profit (% from entry)")
|
|
22
|
+
.option("--sl <pct>", "Attach stop-loss (% from entry)")
|
|
23
|
+
.option("--no-close-then-flip", "Disable auto close-then-flip behavior")
|
|
24
|
+
.option("--idempotency-key <key>", "Idempotency key for safe retries")
|
|
25
|
+
.action(async function (side, symbol, size, price) {
|
|
26
|
+
const ctx = getContext(this);
|
|
27
|
+
const outputOpts = getOutputOptions(this);
|
|
28
|
+
const exchangeId = getSelectedExchange(this);
|
|
29
|
+
const opts = this.opts();
|
|
30
|
+
try {
|
|
31
|
+
// Normalize inputs
|
|
32
|
+
const market = normalizeMarket(symbol);
|
|
33
|
+
const requestedSize = parseFloat(size);
|
|
34
|
+
if (!Number.isFinite(requestedSize) || requestedSize <= 0) {
|
|
35
|
+
throw new Error("Size must be a positive number");
|
|
36
|
+
}
|
|
37
|
+
const limitPrice = parseFloat(price);
|
|
38
|
+
if (!Number.isFinite(limitPrice) || limitPrice <= 0) {
|
|
39
|
+
throw new Error("Price must be a positive number");
|
|
40
|
+
}
|
|
41
|
+
const normalizedSide = normalizeSide(side);
|
|
42
|
+
const closeThenFlip = opts.closeThenFlip ?? true;
|
|
43
|
+
const confidence = opts.confidence ? parseFloat(opts.confidence) : 1;
|
|
44
|
+
if (!Number.isFinite(confidence) || confidence < 0 || confidence > 1) {
|
|
45
|
+
throw new Error("Confidence must be a number between 0 and 1");
|
|
46
|
+
}
|
|
47
|
+
const shouldAttachTpSl = opts.tp !== undefined || opts.sl !== undefined;
|
|
48
|
+
const tp = opts.tp !== undefined ? parseFloat(opts.tp) : ctx.config.executionSafety.takeProfitPct;
|
|
49
|
+
const sl = opts.sl !== undefined ? parseFloat(opts.sl) : ctx.config.executionSafety.stopLossPct;
|
|
50
|
+
if (shouldAttachTpSl) {
|
|
51
|
+
if (!Number.isFinite(tp) || tp <= 0)
|
|
52
|
+
throw new Error("TP must be a positive number");
|
|
53
|
+
if (!Number.isFinite(sl) || sl <= 0)
|
|
54
|
+
throw new Error("SL must be a positive number");
|
|
55
|
+
}
|
|
56
|
+
const adapter = getExchangeAdapter();
|
|
57
|
+
const credentials = getExchangeCredentials(ctx.config, exchangeId, {
|
|
58
|
+
requireTrading: true,
|
|
59
|
+
});
|
|
60
|
+
await adapter.connect({
|
|
61
|
+
testnet: ctx.config.testnet,
|
|
62
|
+
rpcUrl: credentials.fullnodeUrl,
|
|
63
|
+
wsUrl: credentials.wsUrl,
|
|
64
|
+
credentials,
|
|
65
|
+
});
|
|
66
|
+
try {
|
|
67
|
+
const riskPolicy = new RiskPolicyMiddleware(ctx.config, exchangeId);
|
|
68
|
+
const riskDecision = await riskPolicy.evaluateOrder(adapter, {
|
|
69
|
+
market,
|
|
70
|
+
side: normalizedSide,
|
|
71
|
+
requestedSizeBase: requestedSize,
|
|
72
|
+
referencePriceUsd: limitPrice,
|
|
73
|
+
confidence,
|
|
74
|
+
reason: "cli:order:limit",
|
|
75
|
+
});
|
|
76
|
+
if (!riskDecision.allowed || riskDecision.adjustedSizeBase <= 0) {
|
|
77
|
+
throw new Error(riskDecision.reason ?? "Order blocked by risk policy");
|
|
78
|
+
}
|
|
79
|
+
const finalSize = riskDecision.adjustedSizeBase;
|
|
80
|
+
const journaled = await runWithExecutionJournal({
|
|
81
|
+
idempotencyKey: opts.idempotencyKey,
|
|
82
|
+
metadata: {
|
|
83
|
+
command: "order.limit",
|
|
84
|
+
exchange: exchangeId,
|
|
85
|
+
testnet: ctx.config.testnet,
|
|
86
|
+
market,
|
|
87
|
+
side: normalizedSide,
|
|
88
|
+
orderType: "limit",
|
|
89
|
+
},
|
|
90
|
+
request: {
|
|
91
|
+
market,
|
|
92
|
+
side: normalizedSide,
|
|
93
|
+
requestedSize: requestedSize.toFixed(8),
|
|
94
|
+
limitPrice: limitPrice.toFixed(8),
|
|
95
|
+
reduceOnly: opts.reduceOnly ?? false,
|
|
96
|
+
postOnly: opts.postOnly ?? false,
|
|
97
|
+
confidence,
|
|
98
|
+
closeThenFlip,
|
|
99
|
+
tp: shouldAttachTpSl ? tp : null,
|
|
100
|
+
sl: shouldAttachTpSl ? sl : null,
|
|
101
|
+
},
|
|
102
|
+
execute: async () => {
|
|
103
|
+
const execution = await executeOrderWithSafety(adapter, {
|
|
104
|
+
market,
|
|
105
|
+
side: normalizedSide,
|
|
106
|
+
type: "limit",
|
|
107
|
+
size: finalSize.toFixed(8),
|
|
108
|
+
price: limitPrice.toString(),
|
|
109
|
+
reduceOnly: opts.reduceOnly,
|
|
110
|
+
postOnly: opts.postOnly,
|
|
111
|
+
}, {
|
|
112
|
+
closeThenFlip,
|
|
113
|
+
spreadAwarePricing: false,
|
|
114
|
+
attachTpSl: shouldAttachTpSl,
|
|
115
|
+
tpSlConfig: shouldAttachTpSl
|
|
116
|
+
? { takeProfitPct: tp, stopLossPct: sl }
|
|
117
|
+
: undefined,
|
|
118
|
+
});
|
|
119
|
+
return {
|
|
120
|
+
order: execution.order,
|
|
121
|
+
risk: {
|
|
122
|
+
requestedSize,
|
|
123
|
+
adjustedSize: finalSize,
|
|
124
|
+
adjustedNotionalUsd: riskDecision.adjustedSizeUsd,
|
|
125
|
+
referencePrice: limitPrice,
|
|
126
|
+
},
|
|
127
|
+
safety: {
|
|
128
|
+
closedOppositePosition: execution.closedOppositePosition,
|
|
129
|
+
tpSlOrderIds: execution.tpSlOrderIds,
|
|
130
|
+
},
|
|
131
|
+
};
|
|
132
|
+
},
|
|
133
|
+
});
|
|
134
|
+
const response = {
|
|
135
|
+
...journaled.result.order,
|
|
136
|
+
risk: journaled.result.risk,
|
|
137
|
+
safety: journaled.result.safety,
|
|
138
|
+
idempotency: {
|
|
139
|
+
key: journaled.idempotencyKey,
|
|
140
|
+
replayed: journaled.replayed,
|
|
141
|
+
autoGenerated: journaled.autoGeneratedKey,
|
|
142
|
+
journalId: journaled.journalId,
|
|
143
|
+
},
|
|
144
|
+
};
|
|
145
|
+
if (outputOpts.json) {
|
|
146
|
+
output(withJsonContract("order.execution.result", {
|
|
147
|
+
executionStatus: journaled.replayed ? "replayed" : "executed",
|
|
148
|
+
...response,
|
|
149
|
+
}), outputOpts);
|
|
150
|
+
}
|
|
151
|
+
else {
|
|
152
|
+
outputSuccess(journaled.replayed ? "Order replayed from idempotency journal!" : "Order placed!");
|
|
153
|
+
console.log(` Order ID: ${response.id}`);
|
|
154
|
+
console.log(` Market: ${response.market}`);
|
|
155
|
+
console.log(` Side: ${response.side.toUpperCase()}`);
|
|
156
|
+
console.log(` Size: ${response.size}`);
|
|
157
|
+
console.log(` Price: $${response.price}`);
|
|
158
|
+
console.log(` Status: ${response.status}`);
|
|
159
|
+
if (Math.abs(response.risk.adjustedSize - response.risk.requestedSize) > 1e-12) {
|
|
160
|
+
console.log(` Risk Size: adjusted from ${response.risk.requestedSize} to ${response.risk.adjustedSize.toFixed(8)}`);
|
|
161
|
+
}
|
|
162
|
+
if (response.safety.closedOppositePosition) {
|
|
163
|
+
console.log(" Safety: closed opposite position before opening new side");
|
|
164
|
+
}
|
|
165
|
+
if (response.safety.tpSlOrderIds.length > 0) {
|
|
166
|
+
console.log(` TP/SL: attached (${response.safety.tpSlOrderIds.join(", ")})`);
|
|
167
|
+
}
|
|
168
|
+
console.log(` Idempotency: ${response.idempotency.key}` +
|
|
169
|
+
(response.idempotency.replayed ? " (replayed)" : ""));
|
|
170
|
+
console.log("");
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
finally {
|
|
174
|
+
await adapter.disconnect();
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
catch (err) {
|
|
178
|
+
const code = inferExitCode(err);
|
|
179
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
180
|
+
if (outputOpts.json) {
|
|
181
|
+
output(withJsonContract("order.execution.result", {
|
|
182
|
+
status: "error",
|
|
183
|
+
error: {
|
|
184
|
+
message,
|
|
185
|
+
exitCode: code,
|
|
186
|
+
},
|
|
187
|
+
}), outputOpts);
|
|
188
|
+
}
|
|
189
|
+
else {
|
|
190
|
+
outputError(message);
|
|
191
|
+
}
|
|
192
|
+
process.exit(code);
|
|
193
|
+
}
|
|
194
|
+
});
|
|
195
|
+
}
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Simple market order command using the adapter interface
|
|
3
|
+
*/
|
|
4
|
+
import { getContext, getOutputOptions, getSelectedExchange } from "../../cli/program.js";
|
|
5
|
+
import { output, outputError, outputSuccess } from "../../cli/output.js";
|
|
6
|
+
import { getExchangeAdapter } from "../../lib/exchange.js";
|
|
7
|
+
import { getExchangeCredentials } from "../../lib/config.js";
|
|
8
|
+
import { RiskPolicyMiddleware } from "../../lib/risk/policy-middleware.js";
|
|
9
|
+
import { executeOrderWithSafety } from "../../lib/execution/safety.js";
|
|
10
|
+
import { runWithExecutionJournal } from "../../lib/execution/journal.js";
|
|
11
|
+
import { normalizeMarket, normalizeSide } from "./shared.js";
|
|
12
|
+
import { withJsonContract } from "../../lib/contracts.js";
|
|
13
|
+
import { inferExitCode } from "../../lib/exit-codes.js";
|
|
14
|
+
export function registerMarketOrderSimpleCommand(order) {
|
|
15
|
+
order
|
|
16
|
+
.command("market <side> <symbol> <size>")
|
|
17
|
+
.description("Place a market order (side: buy/long or sell/short)")
|
|
18
|
+
.option("--reduce-only", "Reduce-only order")
|
|
19
|
+
.option("--confidence <0-1>", "Signal confidence for risk policy (default: 1.0)")
|
|
20
|
+
.option("--tp <pct>", "Attach take-profit (% from entry)")
|
|
21
|
+
.option("--sl <pct>", "Attach stop-loss (% from entry)")
|
|
22
|
+
.option("--no-close-then-flip", "Disable auto close-then-flip behavior")
|
|
23
|
+
.option("--no-spread-aware", "Disable spread-aware execution pricing")
|
|
24
|
+
.option("--idempotency-key <key>", "Idempotency key for safe retries")
|
|
25
|
+
.action(async function (side, symbol, size) {
|
|
26
|
+
const ctx = getContext(this);
|
|
27
|
+
const outputOpts = getOutputOptions(this);
|
|
28
|
+
const exchangeId = getSelectedExchange(this);
|
|
29
|
+
const opts = this.opts();
|
|
30
|
+
try {
|
|
31
|
+
// Normalize inputs
|
|
32
|
+
const market = normalizeMarket(symbol);
|
|
33
|
+
const requestedSize = parseFloat(size);
|
|
34
|
+
if (!Number.isFinite(requestedSize) || requestedSize <= 0) {
|
|
35
|
+
throw new Error("Size must be a positive number");
|
|
36
|
+
}
|
|
37
|
+
const normalizedSide = normalizeSide(side);
|
|
38
|
+
const closeThenFlip = opts.closeThenFlip ?? true;
|
|
39
|
+
const spreadAware = opts.spreadAware ?? true;
|
|
40
|
+
const confidence = opts.confidence ? parseFloat(opts.confidence) : 1;
|
|
41
|
+
if (!Number.isFinite(confidence) || confidence < 0 || confidence > 1) {
|
|
42
|
+
throw new Error("Confidence must be a number between 0 and 1");
|
|
43
|
+
}
|
|
44
|
+
const shouldAttachTpSl = opts.tp !== undefined || opts.sl !== undefined;
|
|
45
|
+
const tp = opts.tp !== undefined ? parseFloat(opts.tp) : ctx.config.executionSafety.takeProfitPct;
|
|
46
|
+
const sl = opts.sl !== undefined ? parseFloat(opts.sl) : ctx.config.executionSafety.stopLossPct;
|
|
47
|
+
if (shouldAttachTpSl) {
|
|
48
|
+
if (!Number.isFinite(tp) || tp <= 0)
|
|
49
|
+
throw new Error("TP must be a positive number");
|
|
50
|
+
if (!Number.isFinite(sl) || sl <= 0)
|
|
51
|
+
throw new Error("SL must be a positive number");
|
|
52
|
+
}
|
|
53
|
+
const adapter = getExchangeAdapter();
|
|
54
|
+
const credentials = getExchangeCredentials(ctx.config, exchangeId, {
|
|
55
|
+
requireTrading: true,
|
|
56
|
+
});
|
|
57
|
+
await adapter.connect({
|
|
58
|
+
testnet: ctx.config.testnet,
|
|
59
|
+
rpcUrl: credentials.fullnodeUrl,
|
|
60
|
+
wsUrl: credentials.wsUrl,
|
|
61
|
+
credentials,
|
|
62
|
+
});
|
|
63
|
+
try {
|
|
64
|
+
const riskPolicy = new RiskPolicyMiddleware(ctx.config, exchangeId);
|
|
65
|
+
const riskDecision = await riskPolicy.evaluateOrder(adapter, {
|
|
66
|
+
market,
|
|
67
|
+
side: normalizedSide,
|
|
68
|
+
requestedSizeBase: requestedSize,
|
|
69
|
+
confidence,
|
|
70
|
+
reason: "cli:order:market",
|
|
71
|
+
});
|
|
72
|
+
if (!riskDecision.allowed || riskDecision.adjustedSizeBase <= 0) {
|
|
73
|
+
throw new Error(riskDecision.reason ?? "Order blocked by risk policy");
|
|
74
|
+
}
|
|
75
|
+
const finalSize = riskDecision.adjustedSizeBase;
|
|
76
|
+
const journaled = await runWithExecutionJournal({
|
|
77
|
+
idempotencyKey: opts.idempotencyKey,
|
|
78
|
+
metadata: {
|
|
79
|
+
command: "order.market",
|
|
80
|
+
exchange: exchangeId,
|
|
81
|
+
testnet: ctx.config.testnet,
|
|
82
|
+
market,
|
|
83
|
+
side: normalizedSide,
|
|
84
|
+
orderType: "market",
|
|
85
|
+
},
|
|
86
|
+
request: {
|
|
87
|
+
market,
|
|
88
|
+
side: normalizedSide,
|
|
89
|
+
requestedSize: requestedSize.toFixed(8),
|
|
90
|
+
reduceOnly: opts.reduceOnly ?? false,
|
|
91
|
+
confidence,
|
|
92
|
+
closeThenFlip,
|
|
93
|
+
spreadAware,
|
|
94
|
+
spreadOffset: ctx.config.executionSafety.spreadOffset,
|
|
95
|
+
tp: shouldAttachTpSl ? tp : null,
|
|
96
|
+
sl: shouldAttachTpSl ? sl : null,
|
|
97
|
+
},
|
|
98
|
+
execute: async () => {
|
|
99
|
+
const execution = await executeOrderWithSafety(adapter, {
|
|
100
|
+
market,
|
|
101
|
+
side: normalizedSide,
|
|
102
|
+
type: "market",
|
|
103
|
+
size: finalSize.toFixed(8),
|
|
104
|
+
reduceOnly: opts.reduceOnly,
|
|
105
|
+
}, {
|
|
106
|
+
closeThenFlip,
|
|
107
|
+
spreadAwarePricing: spreadAware,
|
|
108
|
+
spreadOffset: ctx.config.executionSafety.spreadOffset,
|
|
109
|
+
attachTpSl: shouldAttachTpSl,
|
|
110
|
+
tpSlConfig: shouldAttachTpSl
|
|
111
|
+
? { takeProfitPct: tp, stopLossPct: sl }
|
|
112
|
+
: undefined,
|
|
113
|
+
});
|
|
114
|
+
return {
|
|
115
|
+
order: execution.order,
|
|
116
|
+
risk: {
|
|
117
|
+
requestedSize,
|
|
118
|
+
adjustedSize: finalSize,
|
|
119
|
+
adjustedNotionalUsd: riskDecision.adjustedSizeUsd,
|
|
120
|
+
referencePrice: riskDecision.referencePriceUsd,
|
|
121
|
+
},
|
|
122
|
+
safety: {
|
|
123
|
+
closedOppositePosition: execution.closedOppositePosition,
|
|
124
|
+
tpSlOrderIds: execution.tpSlOrderIds,
|
|
125
|
+
pricing: execution.pricing,
|
|
126
|
+
},
|
|
127
|
+
};
|
|
128
|
+
},
|
|
129
|
+
});
|
|
130
|
+
const response = {
|
|
131
|
+
...journaled.result.order,
|
|
132
|
+
risk: journaled.result.risk,
|
|
133
|
+
safety: journaled.result.safety,
|
|
134
|
+
idempotency: {
|
|
135
|
+
key: journaled.idempotencyKey,
|
|
136
|
+
replayed: journaled.replayed,
|
|
137
|
+
autoGenerated: journaled.autoGeneratedKey,
|
|
138
|
+
journalId: journaled.journalId,
|
|
139
|
+
},
|
|
140
|
+
};
|
|
141
|
+
if (outputOpts.json) {
|
|
142
|
+
output(withJsonContract("order.execution.result", {
|
|
143
|
+
executionStatus: journaled.replayed ? "replayed" : "executed",
|
|
144
|
+
...response,
|
|
145
|
+
}), outputOpts);
|
|
146
|
+
}
|
|
147
|
+
else {
|
|
148
|
+
outputSuccess(journaled.replayed ? "Order replayed from idempotency journal!" : "Market order placed!");
|
|
149
|
+
console.log(` Order ID: ${response.id}`);
|
|
150
|
+
console.log(` Market: ${response.market}`);
|
|
151
|
+
console.log(` Side: ${response.side.toUpperCase()}`);
|
|
152
|
+
console.log(` Size: ${response.size}`);
|
|
153
|
+
console.log(` Status: ${response.status}`);
|
|
154
|
+
if (Math.abs(response.risk.adjustedSize - response.risk.requestedSize) > 1e-12) {
|
|
155
|
+
console.log(` Risk Size: adjusted from ${response.risk.requestedSize} to ${response.risk.adjustedSize.toFixed(8)}`);
|
|
156
|
+
}
|
|
157
|
+
if (response.safety.closedOppositePosition) {
|
|
158
|
+
console.log(" Safety: closed opposite position before opening new side");
|
|
159
|
+
}
|
|
160
|
+
if (response.safety.tpSlOrderIds.length > 0) {
|
|
161
|
+
console.log(` TP/SL: attached (${response.safety.tpSlOrderIds.join(", ")})`);
|
|
162
|
+
}
|
|
163
|
+
console.log(` Idempotency: ${response.idempotency.key}` +
|
|
164
|
+
(response.idempotency.replayed ? " (replayed)" : ""));
|
|
165
|
+
console.log("");
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
finally {
|
|
169
|
+
await adapter.disconnect();
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
catch (err) {
|
|
173
|
+
const code = inferExitCode(err);
|
|
174
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
175
|
+
if (outputOpts.json) {
|
|
176
|
+
output(withJsonContract("order.execution.result", {
|
|
177
|
+
status: "error",
|
|
178
|
+
error: {
|
|
179
|
+
message,
|
|
180
|
+
exitCode: code,
|
|
181
|
+
},
|
|
182
|
+
}), outputOpts);
|
|
183
|
+
}
|
|
184
|
+
else {
|
|
185
|
+
outputError(message);
|
|
186
|
+
}
|
|
187
|
+
process.exit(code);
|
|
188
|
+
}
|
|
189
|
+
});
|
|
190
|
+
}
|