perp-cli 0.14.2 → 0.15.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/README.md +152 -13
- package/dist/agent-wallet/store.d.ts +3 -2
- package/dist/agent-wallet/store.js +2 -2
- package/dist/api/public/hyperliquid.js +4 -0
- package/dist/api/public/pacifica.js +83 -13
- package/dist/api/public/urls.d.ts +2 -0
- package/dist/api/public/urls.js +3 -0
- package/dist/arb/utils.d.ts +1 -2
- package/dist/arb/utils.js +1 -2
- package/dist/bot/apex/pulse.js +3 -0
- package/dist/bot/apex/radar.js +2 -1
- package/dist/bot/engine.d.ts +10 -1
- package/dist/bot/engine.js +14 -6
- package/dist/bot/indicators.d.ts +7 -0
- package/dist/bot/indicators.js +16 -0
- package/dist/bot/perp-order.d.ts +4 -4
- package/dist/bot/perp-order.js +5 -5
- package/dist/bot/quarantine.d.ts +1 -1
- package/dist/bot/quarantine.js +1 -1
- package/dist/bot/strategies/apex-strategy.js +3 -2
- package/dist/bot/strategies/engine-mm.js +6 -5
- package/dist/bot/strategies/funding-arb-strategy.js +9 -9
- package/dist/bot/strategies/funding-arb-utils.d.ts +15 -2
- package/dist/bot/strategies/funding-arb-utils.js +43 -24
- package/dist/bot/strategies/funding-auto.js +2 -2
- package/dist/bot/strategies/liquidation-mm.d.ts +1 -0
- package/dist/bot/strategies/liquidation-mm.js +8 -2
- package/dist/bot/strategies/momentum-breakout.d.ts +1 -0
- package/dist/bot/strategies/momentum-breakout.js +11 -3
- package/dist/bot/strategies/spot-perp-arb-strategy.js +23 -12
- package/dist/bot/strategies/spot-perp-executor.d.ts +1 -1
- package/dist/bot/strategies/spot-perp-executor.js +1 -1
- package/dist/bot/strategy-types.d.ts +2 -1
- package/dist/bot/tui/BotDashboard.d.ts +1 -1
- package/dist/bot/tui/BotDashboard.js +1 -1
- package/dist/cache.js +12 -6
- package/dist/cli-program.d.ts +15 -0
- package/dist/cli-program.js +125 -5
- package/dist/commands/agent-import.d.ts +7 -0
- package/dist/commands/agent-import.js +30 -1
- package/dist/commands/agent.d.ts +6 -3
- package/dist/commands/agent.js +160 -12
- package/dist/commands/arb/index.js +4 -4
- package/dist/commands/arb-auto.js +21 -17
- package/dist/commands/arb.d.ts +12 -0
- package/dist/commands/arb.js +61 -143
- package/dist/commands/bot.js +1 -1
- package/dist/commands/builder.js +1 -1
- package/dist/commands/funds.js +113 -3
- package/dist/commands/init.d.ts +1 -1
- package/dist/commands/init.js +27 -7
- package/dist/commands/manage.js +23 -21
- package/dist/commands/market.js +168 -36
- package/dist/commands/options.d.ts +9 -0
- package/dist/commands/options.js +134 -0
- package/dist/commands/pacifica-earn.d.ts +5 -0
- package/dist/commands/pacifica-earn.js +468 -0
- package/dist/commands/pacifica-orders.d.ts +14 -0
- package/dist/commands/pacifica-orders.js +142 -0
- package/dist/commands/portfolio.js +32 -47
- package/dist/commands/rebalance.d.ts +1 -0
- package/dist/commands/rebalance.js +15 -0
- package/dist/commands/risk.d.ts +11 -2
- package/dist/commands/risk.js +39 -44
- package/dist/commands/trade.d.ts +1 -0
- package/dist/commands/trade.js +63 -58
- package/dist/commands/wallet.js +1 -1
- package/dist/config.d.ts +2 -2
- package/dist/config.js +15 -4
- package/dist/constants.js +6 -0
- package/dist/dashboard/server.js +1 -1
- package/dist/dashboard/ui.js +1 -1
- package/dist/errors.d.ts +24 -1
- package/dist/errors.js +25 -3
- package/dist/exchanges/arcus/adapter.d.ts +7 -7
- package/dist/exchanges/arcus/adapter.js +12 -12
- package/dist/exchanges/arcus/agent.d.ts +1 -1
- package/dist/exchanges/arcus/agent.js +1 -1
- package/dist/exchanges/arcus/client.js +1 -1
- package/dist/exchanges/arcus/signing.d.ts +2 -3
- package/dist/exchanges/arcus/signing.js +1 -1
- package/dist/exchanges/arcus/spot-chain.d.ts +1 -1
- package/dist/exchanges/arcus/spot-chain.js +1 -1
- package/dist/exchanges/arcus/spot-router.d.ts +1 -1
- package/dist/exchanges/arcus/spot-router.js +2 -2
- package/dist/exchanges/arcus/spot.d.ts +2 -3
- package/dist/exchanges/arcus/spot.js +2 -3
- package/dist/exchanges/aster-spot.d.ts +3 -3
- package/dist/exchanges/aster-spot.js +6 -9
- package/dist/exchanges/aster-typed-data.d.ts +1 -1
- package/dist/exchanges/aster-typed-data.js +1 -1
- package/dist/exchanges/aster.d.ts +9 -4
- package/dist/exchanges/aster.js +125 -55
- package/dist/exchanges/builder-fee.d.ts +1 -1
- package/dist/exchanges/builder-fee.js +13 -2
- package/dist/exchanges/canonical-symbols.d.ts +1 -2
- package/dist/exchanges/canonical-symbols.js +26 -2
- package/dist/exchanges/derive/client.d.ts +37 -0
- package/dist/exchanges/derive/client.js +135 -0
- package/dist/exchanges/derive/options.d.ts +68 -0
- package/dist/exchanges/derive/options.js +360 -0
- package/dist/exchanges/extended/adapter.d.ts +82 -0
- package/dist/exchanges/extended/adapter.js +179 -0
- package/dist/exchanges/extended/client.d.ts +38 -0
- package/dist/exchanges/extended/client.js +88 -0
- package/dist/exchanges/extended/public.d.ts +130 -0
- package/dist/exchanges/extended/public.js +429 -0
- package/dist/exchanges/grvt/adapter.d.ts +86 -0
- package/dist/exchanges/grvt/adapter.js +206 -0
- package/dist/exchanges/grvt/client.d.ts +25 -0
- package/dist/exchanges/grvt/client.js +86 -0
- package/dist/exchanges/grvt/public.d.ts +151 -0
- package/dist/exchanges/grvt/public.js +438 -0
- package/dist/exchanges/hyperliquid-spot.d.ts +1 -1
- package/dist/exchanges/hyperliquid-spot.js +5 -5
- package/dist/exchanges/hyperliquid.d.ts +22 -16
- package/dist/exchanges/hyperliquid.js +80 -55
- package/dist/exchanges/index.d.ts +1 -1
- package/dist/exchanges/index.js +1 -1
- package/dist/exchanges/interface.d.ts +97 -8
- package/dist/exchanges/interface.js +19 -2
- package/dist/exchanges/ioc-outcome.d.ts +1 -1
- package/dist/exchanges/ioc-outcome.js +1 -1
- package/dist/exchanges/legacy-trading-v2.d.ts +2 -2
- package/dist/exchanges/legacy-trading-v2.js +7 -2
- package/dist/exchanges/lighter-public.d.ts +2 -2
- package/dist/exchanges/lighter-public.js +2 -2
- package/dist/exchanges/lighter-signer.d.ts +1 -1
- package/dist/exchanges/lighter-signer.js +1 -1
- package/dist/exchanges/lighter-spot.d.ts +3 -4
- package/dist/exchanges/lighter-spot.js +6 -7
- package/dist/exchanges/lighter.d.ts +11 -11
- package/dist/exchanges/lighter.js +28 -14
- package/dist/exchanges/ondo/adapter.d.ts +5 -6
- package/dist/exchanges/ondo/adapter.js +16 -14
- package/dist/exchanges/ondo/agent.d.ts +1 -1
- package/dist/exchanges/ondo/agent.js +1 -1
- package/dist/exchanges/ondo/client.d.ts +1 -1
- package/dist/exchanges/ondo/client.js +2 -2
- package/dist/exchanges/ondo/public.d.ts +8 -1
- package/dist/exchanges/ondo/public.js +13 -2
- package/dist/exchanges/ondo/spot.d.ts +2 -2
- package/dist/exchanges/ondo/spot.js +3 -3
- package/dist/exchanges/options-interface.d.ts +158 -0
- package/dist/exchanges/options-interface.js +17 -0
- package/dist/exchanges/options-venues.d.ts +12 -0
- package/dist/exchanges/options-venues.js +35 -0
- package/dist/exchanges/pacifica-atomic.d.ts +211 -0
- package/dist/exchanges/pacifica-atomic.js +440 -0
- package/dist/exchanges/pacifica-decimal.d.ts +10 -0
- package/dist/exchanges/pacifica-decimal.js +51 -0
- package/dist/exchanges/pacifica-presigned.d.ts +53 -0
- package/dist/exchanges/pacifica-presigned.js +141 -0
- package/dist/exchanges/pacifica-print.d.ts +200 -0
- package/dist/exchanges/pacifica-print.js +417 -0
- package/dist/exchanges/pacifica-spot.d.ts +2 -2
- package/dist/exchanges/pacifica-spot.js +3 -3
- package/dist/exchanges/pacifica-typed-data.d.ts +1 -1
- package/dist/exchanges/pacifica-typed-data.js +1 -1
- package/dist/exchanges/pacifica-vaults.d.ts +121 -0
- package/dist/exchanges/pacifica-vaults.js +264 -0
- package/dist/exchanges/pacifica.d.ts +32 -5
- package/dist/exchanges/pacifica.js +114 -48
- package/dist/exchanges/precision.d.ts +3 -1
- package/dist/exchanges/precision.js +9 -2
- package/dist/exchanges/qfex/adapter.d.ts +18 -5
- package/dist/exchanges/qfex/adapter.js +32 -12
- package/dist/exchanges/qfex/agent.d.ts +1 -1
- package/dist/exchanges/qfex/agent.js +1 -1
- package/dist/exchanges/qfex/client.d.ts +1 -1
- package/dist/exchanges/qfex/client.js +1 -1
- package/dist/exchanges/qfex/errors.d.ts +1 -1
- package/dist/exchanges/qfex/errors.js +2 -2
- package/dist/exchanges/qfex/public.d.ts +3 -3
- package/dist/exchanges/qfex/public.js +3 -3
- package/dist/exchanges/qfex/signing.d.ts +2 -2
- package/dist/exchanges/qfex/signing.js +4 -5
- package/dist/exchanges/qfex/ws-transport.d.ts +2 -2
- package/dist/exchanges/qfex/ws-transport.js +3 -3
- package/dist/exchanges/rate-limiter.d.ts +1 -1
- package/dist/exchanges/rate-limiter.js +5 -2
- package/dist/exchanges/registry.d.ts +1 -1
- package/dist/exchanges/registry.js +1 -1
- package/dist/exchanges/risex/adapter.d.ts +148 -0
- package/dist/exchanges/risex/adapter.js +796 -0
- package/dist/exchanges/risex/agent.d.ts +154 -0
- package/dist/exchanges/risex/agent.js +384 -0
- package/dist/exchanges/risex/client.d.ts +47 -0
- package/dist/exchanges/risex/client.js +174 -0
- package/dist/exchanges/risex/nonce.d.ts +43 -0
- package/dist/exchanges/risex/nonce.js +212 -0
- package/dist/exchanges/risex/public.d.ts +64 -0
- package/dist/exchanges/risex/public.js +150 -0
- package/dist/exchanges/risex/session.d.ts +71 -0
- package/dist/exchanges/risex/session.js +171 -0
- package/dist/exchanges/risex/typed-data.d.ts +173 -0
- package/dist/exchanges/risex/typed-data.js +240 -0
- package/dist/exchanges/risex/units.d.ts +29 -0
- package/dist/exchanges/risex/units.js +104 -0
- package/dist/exchanges/slippage.d.ts +1 -1
- package/dist/exchanges/slippage.js +1 -1
- package/dist/exchanges/spot-factory.d.ts +3 -3
- package/dist/exchanges/spot-factory.js +7 -7
- package/dist/exchanges/spot-interface.d.ts +5 -5
- package/dist/exchanges/spot-interface.js +2 -2
- package/dist/exchanges/spot-result.d.ts +3 -3
- package/dist/exchanges/spot-result.js +3 -3
- package/dist/exchanges/trading-v2.d.ts +3 -3
- package/dist/exchanges/trading-v2.js +9 -6
- package/dist/exchanges/txflow/adapter.d.ts +54 -0
- package/dist/exchanges/txflow/adapter.js +118 -0
- package/dist/exchanges/txflow/client.d.ts +47 -0
- package/dist/exchanges/txflow/client.js +153 -0
- package/dist/exchanges/txflow/public.d.ts +76 -0
- package/dist/exchanges/txflow/public.js +284 -0
- package/dist/exchanges/unknown-symbol.d.ts +29 -0
- package/dist/exchanges/unknown-symbol.js +51 -0
- package/dist/exchanges/variational/adapter.d.ts +71 -0
- package/dist/exchanges/variational/adapter.js +137 -0
- package/dist/exchanges/variational/client.d.ts +21 -0
- package/dist/exchanges/variational/client.js +66 -0
- package/dist/exchanges/variational/public.d.ts +126 -0
- package/dist/exchanges/variational/public.js +430 -0
- package/dist/exchanges/venue-types.d.ts +6 -7
- package/dist/exchanges/venue-types.js +2 -2
- package/dist/exchanges/venue.d.ts +51 -8
- package/dist/exchanges/venue.js +54 -4
- package/dist/exchanges/venues/arcus.d.ts +1 -2
- package/dist/exchanges/venues/arcus.js +5 -4
- package/dist/exchanges/venues/aster.d.ts +2 -2
- package/dist/exchanges/venues/aster.js +6 -4
- package/dist/exchanges/venues/create-helpers.d.ts +1 -1
- package/dist/exchanges/venues/create-helpers.js +3 -3
- package/dist/exchanges/venues/extended.d.ts +21 -0
- package/dist/exchanges/venues/extended.js +96 -0
- package/dist/exchanges/venues/grvt.d.ts +30 -0
- package/dist/exchanges/venues/grvt.js +98 -0
- package/dist/exchanges/venues/hyperliquid.d.ts +2 -2
- package/dist/exchanges/venues/hyperliquid.js +10 -8
- package/dist/exchanges/venues/index.d.ts +7 -2
- package/dist/exchanges/venues/index.js +8 -2
- package/dist/exchanges/venues/lighter-family.d.ts +7 -9
- package/dist/exchanges/venues/lighter-family.js +9 -11
- package/dist/exchanges/venues/lighter-rh.d.ts +2 -2
- package/dist/exchanges/venues/lighter-rh.js +7 -5
- package/dist/exchanges/venues/lighter.d.ts +1 -1
- package/dist/exchanges/venues/lighter.js +5 -3
- package/dist/exchanges/venues/ondo.d.ts +1 -1
- package/dist/exchanges/venues/ondo.js +5 -3
- package/dist/exchanges/venues/pacifica.d.ts +2 -2
- package/dist/exchanges/venues/pacifica.js +6 -4
- package/dist/exchanges/venues/qfex.d.ts +1 -1
- package/dist/exchanges/venues/qfex.js +4 -2
- package/dist/exchanges/venues/risex-release.d.ts +14 -0
- package/dist/exchanges/venues/risex-release.js +14 -0
- package/dist/exchanges/venues/risex.d.ts +31 -0
- package/dist/exchanges/venues/risex.js +116 -0
- package/dist/exchanges/venues/txflow.d.ts +18 -0
- package/dist/exchanges/venues/txflow.js +77 -0
- package/dist/exchanges/venues/variational.d.ts +21 -0
- package/dist/exchanges/venues/variational.js +85 -0
- package/dist/execution-log.d.ts +1 -1
- package/dist/funding/legacy-public.d.ts +1 -1
- package/dist/funding/legacy-public.js +9 -4
- package/dist/funding/normalize.d.ts +1 -1
- package/dist/funding/normalize.js +1 -1
- package/dist/funding/rates.d.ts +44 -4
- package/dist/funding/rates.js +56 -12
- package/dist/index.js +7 -6
- package/dist/jobs.d.ts +1 -0
- package/dist/jobs.js +25 -1
- package/dist/landing.d.ts +2 -0
- package/dist/landing.js +1 -1
- package/dist/mcp-pacifica-earn.d.ts +9 -0
- package/dist/mcp-pacifica-earn.js +58 -0
- package/dist/mcp-server.js +113 -59
- package/dist/pacifica/client.d.ts +67 -4
- package/dist/pacifica/client.js +170 -8
- package/dist/pacifica/rate-limit.d.ts +144 -0
- package/dist/pacifica/rate-limit.js +267 -0
- package/dist/pacifica/types/order.d.ts +2 -1
- package/dist/plan-executor.js +7 -2
- package/dist/rebalance.js +1 -1
- package/dist/risk.d.ts +2 -1
- package/dist/risk.js +2 -4
- package/dist/settings.d.ts +32 -3
- package/dist/signer/ows-evm.d.ts +1 -1
- package/dist/signer/ows-evm.js +1 -1
- package/dist/smart-order.d.ts +1 -1
- package/dist/smart-order.js +1 -1
- package/dist/strategies/funding-arb.d.ts +1 -1
- package/dist/strategies/funding-arb.js +1 -1
- package/dist/trade-validator.d.ts +1 -1
- package/dist/trade-validator.js +9 -4
- package/dist/utils.d.ts +12 -3
- package/dist/utils.js +11 -3
- package/dist/venue-health.d.ts +40 -0
- package/dist/venue-health.js +75 -0
- package/dist/withdraw-gate.d.ts +1 -1
- package/dist/withdraw-gate.js +1 -1
- package/dist/ws/index.d.ts +959 -0
- package/dist/ws/index.js +4295 -0
- package/dist/ws/node.d.ts +975 -0
- package/dist/ws/node.js +4366 -0
- package/dist/ws/pacifica-instrument.d.ts +8 -0
- package/dist/ws/pacifica-instrument.js +8 -0
- package/dist/ws-cli/credentials.d.ts +62 -0
- package/dist/ws-cli/credentials.js +126 -0
- package/package.json +58 -10
- package/skills/perp-cli/SKILL.md +62 -9
- package/skills/perp-cli/references/agent-operations.md +7 -0
- package/skills/perp-cli/references/commands.md +76 -5
- package/skills/perp-cli/references/ws-library.md +492 -0
package/dist/commands/trade.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { networkOf, supportsDryRun } from "../cli-flags.js";
|
|
2
|
-
import { printJson, errorAndExit, withJsonErrors, jsonOk, jsonError, symbolMatch, formatUsd, formatOrderResult, orderResultJson } from "../utils.js";
|
|
2
|
+
import { printJson, errorAndExit, withJsonErrors, jsonOk, jsonError, symbolMatch, formatUsd, formatOrderResult, orderResultJson, normalizeSymbolArg } from "../utils.js";
|
|
3
3
|
import { ERROR_CODES, extractErrorMessage, isDefiniteOrderFailure, parsePositiveDecimal, PerpError } from "../errors.js";
|
|
4
4
|
import { logExecution } from "../execution-log.js";
|
|
5
5
|
import { validateTrade, enforceOrderRisk } from "../trade-validator.js";
|
|
@@ -24,9 +24,10 @@ import { resolveAdapterSlippage, resolveSlippage } from "../exchanges/slippage.j
|
|
|
24
24
|
import { exactUnits, floorSize, floorToStep, lotStepOf, unitsToString } from "../exchanges/precision.js";
|
|
25
25
|
import { isSpotOpenOrdersCapable } from "../exchanges/spot-interface.js";
|
|
26
26
|
import { builderPlanFor } from "../exchanges/builder-fee.js";
|
|
27
|
+
import { registerPacificaOrderCommands } from "./pacifica-orders.js";
|
|
27
28
|
import { HL_MIN_ORDER_VALUE_USD } from "../constants.js";
|
|
28
29
|
import { defaultSpotQuote, readSpotQuoteAvailableFresh, requireSpotAdapter, requireSpotDefinition, transferQuoteToPerp, transferQuoteToSpot, transferShortfallError } from "../exchanges/spot-factory.js";
|
|
29
|
-
// ── Uniform order surface
|
|
30
|
+
// ── Uniform order surface ──
|
|
30
31
|
//
|
|
31
32
|
// Every venue's trade market/buy/sell/limit/stop/edit/cancel goes through TradingV2:
|
|
32
33
|
// native on ondo/arcus, the legacy wrapper (exchanges/legacy-trading-v2.ts) on
|
|
@@ -34,7 +35,7 @@ import { defaultSpotQuote, readSpotQuoteAvailableFresh, requireSpotAdapter, requ
|
|
|
34
35
|
// shape (OrderResult + execution), one printer.
|
|
35
36
|
/** Families with the legacy smart-order path (--smart: IOC at best book ± 1 tick). */
|
|
36
37
|
const LEGACY_ORDER_FAMILIES = new Set(["pacifica", "hyperliquid", "lighter", "aster"]);
|
|
37
|
-
/** Common default time-in-force for every venue (
|
|
38
|
+
/** Common default time-in-force for every venue (venues map GTC internally, e.g. GTT). */
|
|
38
39
|
const DEFAULT_TIF = "GTC";
|
|
39
40
|
function tradingVenueOf(adapter) {
|
|
40
41
|
const def = getVenue(adapter.name);
|
|
@@ -43,7 +44,7 @@ function tradingVenueOf(adapter) {
|
|
|
43
44
|
function isLegacyOrderVenue(def) {
|
|
44
45
|
return LEGACY_ORDER_FAMILIES.has(def.family);
|
|
45
46
|
}
|
|
46
|
-
function parseSlippageBpsFlag(raw) {
|
|
47
|
+
export function parseSlippageBpsFlag(raw) {
|
|
47
48
|
if (raw === undefined)
|
|
48
49
|
return null;
|
|
49
50
|
const n = Number(raw);
|
|
@@ -68,11 +69,13 @@ const V2_CLIENT_ID_RULES = {
|
|
|
68
69
|
pacifica: { re: PACIFICA_UUID_RE, desc: "a full UUID, e.g. f47ac10b-58cc-4372-a567-0e02b2c3d479" },
|
|
69
70
|
hyperliquid: { re: HL_CLOID_RE, desc: "a 128-bit hex cloid: 0x + 32 lowercase hex chars, e.g. 0x1234567890abcdef1234567890abcdef (or --auto-id)" },
|
|
70
71
|
aster: { re: ASTER_CLIENT_ID_RE, desc: "1-36 chars of A-Z a-z 0-9 . : / _ -" },
|
|
72
|
+
// uint64 decimal (range checked by the adapter).
|
|
73
|
+
risex: { re: /^[1-9][0-9]{0,19}$/, desc: "a decimal integer from 1 to 2^64 - 1" },
|
|
71
74
|
};
|
|
72
75
|
/**
|
|
73
76
|
* Venues whose client order ids perp-cli GENERATES and never takes from the user: QFEX ids are
|
|
74
77
|
* not unique on the venue, so a reused id would attribute another order's fills / cancels to
|
|
75
|
-
* this one (
|
|
78
|
+
* this one (c). --auto-id still works (the generated id is reported).
|
|
76
79
|
*/
|
|
77
80
|
const GENERATED_CLIENT_ID_ONLY = new Set(["qfex"]);
|
|
78
81
|
function validateV2ClientId(def, id) {
|
|
@@ -111,7 +114,7 @@ function v2ClientOrderId(def, caps, userId) {
|
|
|
111
114
|
* --auto-id: venue-format id where the venue has client ids; a venue without them refuses it
|
|
112
115
|
* (v2ClientOrderId — the locally generated id is never sent).
|
|
113
116
|
* Ondo / Pacifica limit orders: undefined — the adapter generates its own id, which encodes
|
|
114
|
-
* post-only (Ondo) / the TIF (Pacifica) for a later edit
|
|
117
|
+
* post-only (Ondo) / the TIF (Pacifica) for a later edit; the result
|
|
115
118
|
* reports that id. A Pacifica market order has no generated id, so --auto-id sends a UUID.
|
|
116
119
|
* Generated-only venues (qfex): undefined — v2ClientOrderId generates the venue id itself.
|
|
117
120
|
*/
|
|
@@ -314,7 +317,7 @@ function positionTpSlAdapter(adapter) {
|
|
|
314
317
|
}
|
|
315
318
|
return adapter;
|
|
316
319
|
}
|
|
317
|
-
/** Refuse when position-level TP/SL orders already exist (never overwrite
|
|
320
|
+
/** Refuse when position-level TP/SL orders already exist (never overwrite). */
|
|
318
321
|
async function refuseExistingPositionTpSl(adapter, def, symbol) {
|
|
319
322
|
const existing = await adapter.positionTpSlOrders(symbol);
|
|
320
323
|
if (existing.length > 0) {
|
|
@@ -439,7 +442,7 @@ export function registerTradeCommands(program, getAdapter, isJson, isDryRun = ()
|
|
|
439
442
|
logExecution({
|
|
440
443
|
type: action.includes("split") ? "split_order" : action.includes("limit") ? "limit_order" : action.includes("stop") ? "stop_order" : action.includes("cancel") ? "cancel" : "market_order",
|
|
441
444
|
exchange: details.exchange ?? "unknown",
|
|
442
|
-
symbol: (details.symbol ?? "")
|
|
445
|
+
symbol: normalizeSymbolArg(details.symbol ?? ""),
|
|
443
446
|
side: details.side ?? "",
|
|
444
447
|
size: String(details.size ?? details.totalUsd ?? "0"),
|
|
445
448
|
price: details.price,
|
|
@@ -606,7 +609,7 @@ export function registerTradeCommands(program, getAdapter, isJson, isDryRun = ()
|
|
|
606
609
|
console.log();
|
|
607
610
|
}
|
|
608
611
|
/**
|
|
609
|
-
* Validate an entry's --tp/--sl before anything is sent
|
|
612
|
+
* Validate an entry's --tp/--sl before anything is sent: the venue has
|
|
610
613
|
* TP/SL, the entry opens (not reduce-only), separateOrders / positionLevel venues need an
|
|
611
614
|
* immediate entry (market or IOC — a resting entry has no verified fill to size the legs to),
|
|
612
615
|
* and the prices sit on the right sides of `ref`.
|
|
@@ -625,7 +628,7 @@ export function registerTradeCommands(program, getAdapter, isJson, isDryRun = ()
|
|
|
625
628
|
}
|
|
626
629
|
/**
|
|
627
630
|
* Place an entry carrying --tp/--sl — one meaning on every venue: protect THIS entry's
|
|
628
|
-
* verified fill
|
|
631
|
+
* verified fill.
|
|
629
632
|
* - attached (ondo) / batchGroup (arcus): the venue attaches the legs to the entry.
|
|
630
633
|
* - separateOrders (hl, lighter, aster): entry fill VERIFIED (placePerpVerified: venue-final
|
|
631
634
|
* report cross-checked with the position delta), then reduce-only TP/SL triggers sized to it.
|
|
@@ -855,7 +858,7 @@ export function registerTradeCommands(program, getAdapter, isJson, isDryRun = ()
|
|
|
855
858
|
const s = side.toLowerCase();
|
|
856
859
|
if (s !== "buy" && s !== "sell")
|
|
857
860
|
errorAndExit("Side must be buy or sell");
|
|
858
|
-
const sym = symbol
|
|
861
|
+
const sym = normalizeSymbolArg(symbol);
|
|
859
862
|
// ── Split execution branch ──
|
|
860
863
|
if (opts.split) {
|
|
861
864
|
// runSplitOrder has no reduce-only path — refuse rather than silently place opening orders.
|
|
@@ -888,7 +891,7 @@ export function registerTradeCommands(program, getAdapter, isJson, isDryRun = ()
|
|
|
888
891
|
.option("--tp <price>", "Take-profit trigger protecting this entry's verified fill (every venue; separate-order venues need a market / IOC entry)")
|
|
889
892
|
.option("--sl <price>", "Stop-loss trigger protecting this entry's verified fill (every venue; separate-order venues need a market / IOC entry)")
|
|
890
893
|
.action(async (symbol, size, opts) => {
|
|
891
|
-
await runMarketOrder(symbol
|
|
894
|
+
await runMarketOrder(normalizeSymbolArg(symbol), "buy", size, opts);
|
|
892
895
|
});
|
|
893
896
|
supportsDryRun(trade.command("sell <symbol> <size>"))
|
|
894
897
|
.description("Market sell (shortcut for: trade market <symbol> sell <size>)")
|
|
@@ -901,7 +904,7 @@ export function registerTradeCommands(program, getAdapter, isJson, isDryRun = ()
|
|
|
901
904
|
.option("--tp <price>", "Take-profit trigger protecting this entry's verified fill (every venue; separate-order venues need a market / IOC entry)")
|
|
902
905
|
.option("--sl <price>", "Stop-loss trigger protecting this entry's verified fill (every venue; separate-order venues need a market / IOC entry)")
|
|
903
906
|
.action(async (symbol, size, opts) => {
|
|
904
|
-
await runMarketOrder(symbol
|
|
907
|
+
await runMarketOrder(normalizeSymbolArg(symbol), "sell", size, opts);
|
|
905
908
|
});
|
|
906
909
|
// ── Split Order (orderbook-aware) ──
|
|
907
910
|
supportsDryRun(trade.command("split <symbol> <side> <usd>"))
|
|
@@ -912,7 +915,7 @@ export function registerTradeCommands(program, getAdapter, isJson, isDryRun = ()
|
|
|
912
915
|
.option("--min-slice <usd>", "Minimum slice size in USD", "100")
|
|
913
916
|
.option("--force", "Bypass local risk-limit checks (maxPosition / total exposure)")
|
|
914
917
|
.action(async (symbol, side, usd, opts) => {
|
|
915
|
-
const sym = symbol
|
|
918
|
+
const sym = normalizeSymbolArg(symbol);
|
|
916
919
|
const orderSide = side.toLowerCase();
|
|
917
920
|
const totalUsd = parseFloat(usd);
|
|
918
921
|
if (!["buy", "sell"].includes(orderSide))
|
|
@@ -946,7 +949,7 @@ export function registerTradeCommands(program, getAdapter, isJson, isDryRun = ()
|
|
|
946
949
|
const s = side.toLowerCase();
|
|
947
950
|
if (s !== "buy" && s !== "sell")
|
|
948
951
|
errorAndExit("Side must be buy or sell");
|
|
949
|
-
const sym = symbol
|
|
952
|
+
const sym = normalizeSymbolArg(symbol);
|
|
950
953
|
// One parsed size for the risk check and the order ("1,000" / "abc" refused up front).
|
|
951
954
|
const size = parsePositiveDecimal(rawSize, "size");
|
|
952
955
|
if (opts.clientId && isOrderDuplicate(opts.clientId)) {
|
|
@@ -1062,7 +1065,7 @@ export function registerTradeCommands(program, getAdapter, isJson, isDryRun = ()
|
|
|
1062
1065
|
};
|
|
1063
1066
|
if (orderId) {
|
|
1064
1067
|
// cancel <symbol> <orderId>
|
|
1065
|
-
await cancelOne(symbolOrOrderId
|
|
1068
|
+
await cancelOne(normalizeSymbolArg(symbolOrOrderId), orderId);
|
|
1066
1069
|
return;
|
|
1067
1070
|
}
|
|
1068
1071
|
// Single argument — an order reference (c:<clientOrderId>, numeric / UUID / 0x-hex order id)
|
|
@@ -1095,7 +1098,7 @@ export function registerTradeCommands(program, getAdapter, isJson, isDryRun = ()
|
|
|
1095
1098
|
return;
|
|
1096
1099
|
}
|
|
1097
1100
|
// cancel <symbol> — cancel all open orders for this symbol
|
|
1098
|
-
const symbol = symbolOrOrderId
|
|
1101
|
+
const symbol = normalizeSymbolArg(symbolOrOrderId);
|
|
1099
1102
|
const orders = await adapter.getOpenOrders();
|
|
1100
1103
|
const matching = orders.filter((o) => symbolMatch(o.symbol, symbol));
|
|
1101
1104
|
if (matching.length === 0) {
|
|
@@ -1167,16 +1170,16 @@ export function registerTradeCommands(program, getAdapter, isJson, isDryRun = ()
|
|
|
1167
1170
|
const bgAdapter = await getAdapter();
|
|
1168
1171
|
const exchange = bgAdapter.name;
|
|
1169
1172
|
// Risk check (and its --force bypass) runs here: the child `strategy twap` has none.
|
|
1170
|
-
await openingOrderGate(bgAdapter, { symbol: symbol
|
|
1173
|
+
await openingOrderGate(bgAdapter, { symbol: normalizeSymbolArg(symbol), size: Number(size), reduceOnly: opts.reduceOnly, force: opts.force });
|
|
1171
1174
|
const cliArgs = [
|
|
1172
|
-
"strategy", "twap", symbol
|
|
1175
|
+
"strategy", "twap", normalizeSymbolArg(symbol), s, size, duration,
|
|
1173
1176
|
...(opts.slices ? ["--slices", opts.slices] : []),
|
|
1174
1177
|
...(opts.reduceOnly ? ["--reduce-only"] : []),
|
|
1175
1178
|
];
|
|
1176
1179
|
const job = startJob({
|
|
1177
1180
|
strategy: "twap",
|
|
1178
1181
|
exchange,
|
|
1179
|
-
params: { symbol: symbol
|
|
1182
|
+
params: { symbol: normalizeSymbolArg(symbol), side: s, size, duration, slices: opts.slices, reduceOnly: opts.reduceOnly === true },
|
|
1180
1183
|
venue: exchange, network: networkOf(program), dryRun: isDryRun(), selectors: jobSelectorsOf(program),
|
|
1181
1184
|
cliArgs,
|
|
1182
1185
|
});
|
|
@@ -1190,17 +1193,17 @@ export function registerTradeCommands(program, getAdapter, isJson, isDryRun = ()
|
|
|
1190
1193
|
return;
|
|
1191
1194
|
}
|
|
1192
1195
|
const adapter = await getAdapter();
|
|
1193
|
-
await openingOrderGate(adapter, { symbol: symbol
|
|
1196
|
+
await openingOrderGate(adapter, { symbol: normalizeSymbolArg(symbol), size: Number(size), reduceOnly: opts.reduceOnly, force: opts.force });
|
|
1194
1197
|
// Native TWAP: Pacifica (SDK) or Hyperliquid (twapOrder)
|
|
1195
1198
|
// Fallback: client-side TWAP for any exchange without native support
|
|
1196
1199
|
let result;
|
|
1197
1200
|
if (hasPacificaSdk(adapter)) {
|
|
1198
|
-
// One slippage resolver
|
|
1201
|
+
// One slippage resolver: bps → Pacifica's slippage_percent.
|
|
1199
1202
|
const slippage = resolveAdapterSlippage(adapter, parseSlippageBpsFlag(opts.slippageBps));
|
|
1200
1203
|
requirePacificaTwapSigner(adapter, "TWAP order");
|
|
1201
1204
|
const sdk = adapter.sdk;
|
|
1202
1205
|
result = await sdk.createTWAP({
|
|
1203
|
-
symbol: symbol
|
|
1206
|
+
symbol: normalizeSymbolArg(symbol),
|
|
1204
1207
|
amount: size,
|
|
1205
1208
|
side: s === "buy" ? "bid" : "ask",
|
|
1206
1209
|
slippage_percent: String(slippage.bps / 100),
|
|
@@ -1213,13 +1216,13 @@ export function registerTradeCommands(program, getAdapter, isJson, isDryRun = ()
|
|
|
1213
1216
|
if (!/^\d+$/.test(duration.trim()))
|
|
1214
1217
|
errorAndExit("HL TWAP duration must be a whole number of minutes");
|
|
1215
1218
|
const minutes = Number(duration.trim());
|
|
1216
|
-
result = await adapter.twapOrder(symbol
|
|
1219
|
+
result = await adapter.twapOrder(normalizeSymbolArg(symbol), s, size, minutes, { reduceOnly: opts.reduceOnly });
|
|
1217
1220
|
}
|
|
1218
1221
|
else {
|
|
1219
1222
|
// Client-side TWAP (foreground) for exchanges without native TWAP
|
|
1220
1223
|
const { runTWAP } = await import("../strategies/twap.js");
|
|
1221
1224
|
const twapResult = await runTWAP(adapter, {
|
|
1222
|
-
symbol: symbol
|
|
1225
|
+
symbol: normalizeSymbolArg(symbol),
|
|
1223
1226
|
side: s,
|
|
1224
1227
|
totalSize: parseFloat(size),
|
|
1225
1228
|
durationSec: parseInt(duration),
|
|
@@ -1233,7 +1236,7 @@ export function registerTradeCommands(program, getAdapter, isJson, isDryRun = ()
|
|
|
1233
1236
|
}
|
|
1234
1237
|
if (isJson())
|
|
1235
1238
|
return printJson(jsonOk(result));
|
|
1236
|
-
console.log(chalk.green(`\n TWAP ${s.toUpperCase()} ${size} ${symbol
|
|
1239
|
+
console.log(chalk.green(`\n TWAP ${s.toUpperCase()} ${size} ${normalizeSymbolArg(symbol)} over ${duration} placed on ${adapter.name}.\n`));
|
|
1237
1240
|
});
|
|
1238
1241
|
// === Stop / Trigger orders (every trading venue) ===
|
|
1239
1242
|
supportsDryRun(trade.command("stop <symbol> <side> <size> <stopPrice>"))
|
|
@@ -1251,7 +1254,7 @@ export function registerTradeCommands(program, getAdapter, isJson, isDryRun = ()
|
|
|
1251
1254
|
if (opts.triggerType && !["mark", "last", "mid"].includes(opts.triggerType)) {
|
|
1252
1255
|
errorAndExit("--trigger-type must be one of: mark, last, mid");
|
|
1253
1256
|
}
|
|
1254
|
-
const sym = symbol
|
|
1257
|
+
const sym = normalizeSymbolArg(symbol);
|
|
1255
1258
|
const adapter = await getAdapter();
|
|
1256
1259
|
const { def: venueDef, caps: venueCaps } = tradingVenueOf(adapter);
|
|
1257
1260
|
const isLimit = opts.limitPrice !== undefined;
|
|
@@ -1317,7 +1320,7 @@ export function registerTradeCommands(program, getAdapter, isJson, isDryRun = ()
|
|
|
1317
1320
|
const tpLimit = positivePriceOpt(opts.tpLimit, "--tp-limit");
|
|
1318
1321
|
if (tpLimit !== null && prices.tp === null)
|
|
1319
1322
|
throw new PerpError("INVALID_PARAMS", "--tp-limit needs --tp");
|
|
1320
|
-
const sym = symbol
|
|
1323
|
+
const sym = normalizeSymbolArg(symbol);
|
|
1321
1324
|
const adapter = await getAdapter();
|
|
1322
1325
|
const { def, caps } = tradingVenueOf(adapter);
|
|
1323
1326
|
if (caps.tpsl === null) {
|
|
@@ -1419,7 +1422,7 @@ export function registerTradeCommands(program, getAdapter, isJson, isDryRun = ()
|
|
|
1419
1422
|
.requiredOption("--levels <levels>", "[required] Comma-separated price:percent pairs (e.g., 72000:25,75000:25,80000:50)")
|
|
1420
1423
|
.option("--size <size>", "Override total position size (default: uses current position)")
|
|
1421
1424
|
.action(async (symbol, opts) => {
|
|
1422
|
-
const sym = symbol
|
|
1425
|
+
const sym = normalizeSymbolArg(symbol);
|
|
1423
1426
|
const adapter = await getAdapter();
|
|
1424
1427
|
if (adapter.name === "ondo") {
|
|
1425
1428
|
// docs.ondoperps.xyz take-profit-and-stop-loss: position TP/SL "Via Risk Management Module
|
|
@@ -1496,9 +1499,9 @@ export function registerTradeCommands(program, getAdapter, isJson, isDryRun = ()
|
|
|
1496
1499
|
const amendTif = opts.tif !== undefined ? validateLegacyTif(venueDef, venueCaps, opts.tif) : null;
|
|
1497
1500
|
if (opts.forceTif === true && amendTif === null)
|
|
1498
1501
|
throw new PerpError("INVALID_PARAMS", "--force-tif needs --tif");
|
|
1499
|
-
if (dryRunGuard("edit_order", { exchange: adapter.name, symbol: symbol
|
|
1502
|
+
if (dryRunGuard("edit_order", { exchange: adapter.name, symbol: normalizeSymbolArg(symbol), orderId, price, size }))
|
|
1500
1503
|
return;
|
|
1501
|
-
const result = await v2Of(adapter).amendOrder({ symbol: symbol
|
|
1504
|
+
const result = await v2Of(adapter).amendOrder({ symbol: normalizeSymbolArg(symbol), ref: parseOrderRef(orderId), price, size, tif: amendTif, forceTif: opts.forceTif === true });
|
|
1502
1505
|
if (isJson())
|
|
1503
1506
|
return printJson(jsonOk(orderResultJson(result)));
|
|
1504
1507
|
printOrder(`Edit of ${orderId} → $${price} x ${size} on ${adapter.name}`, result);
|
|
@@ -1510,7 +1513,7 @@ export function registerTradeCommands(program, getAdapter, isJson, isDryRun = ()
|
|
|
1510
1513
|
const adapter = await getAdapter();
|
|
1511
1514
|
if (adapter.name === "qfex" && isTradingV2(adapter)) {
|
|
1512
1515
|
// QFEX: cancelByRef reads the row first and routes a TAKE_PROFIT / STOP_LOSS row to cancel_stop_order.
|
|
1513
|
-
const sym = symbol
|
|
1516
|
+
const sym = normalizeSymbolArg(symbol);
|
|
1514
1517
|
if (dryRunGuard("cancel_stop", { exchange: adapter.name, symbol: sym, stopOrderId }))
|
|
1515
1518
|
return;
|
|
1516
1519
|
const r = await adapter.cancelByRef(sym, parseOrderRef(stopOrderId));
|
|
@@ -1528,7 +1531,7 @@ export function registerTradeCommands(program, getAdapter, isJson, isDryRun = ()
|
|
|
1528
1531
|
const pac = adapter;
|
|
1529
1532
|
if (typeof pac.cancelStopOrder !== "function") {
|
|
1530
1533
|
const { def } = tradingVenueOf(adapter);
|
|
1531
|
-
throw unsupported(def.displayName, "cancelStop", "stop orders are cancelled like any other order on this venue", `Use: perp trade cancel ${symbol
|
|
1534
|
+
throw unsupported(def.displayName, "cancelStop", "stop orders are cancelled like any other order on this venue", `Use: perp trade cancel ${normalizeSymbolArg(symbol)} <orderId> -e ${def.id}`);
|
|
1532
1535
|
}
|
|
1533
1536
|
const result = await pac.cancelStopOrder(symbol, stopOrderId);
|
|
1534
1537
|
if (isJson())
|
|
@@ -1549,10 +1552,10 @@ export function registerTradeCommands(program, getAdapter, isJson, isDryRun = ()
|
|
|
1549
1552
|
if (hasPacificaSdk(adapter)) {
|
|
1550
1553
|
requirePacificaTwapSigner(adapter, "TWAP cancel");
|
|
1551
1554
|
const sdk = adapter.sdk;
|
|
1552
|
-
result = await sdk.cancelTWAP({ symbol: symbol
|
|
1555
|
+
result = await sdk.cancelTWAP({ symbol: normalizeSymbolArg(symbol), order_id: Number(twapOrderId) }, adapter.publicKey, adapter.signer);
|
|
1553
1556
|
}
|
|
1554
1557
|
else if (isTwapCapable(adapter)) {
|
|
1555
|
-
result = await adapter.twapCancel(symbol
|
|
1558
|
+
result = await adapter.twapCancel(normalizeSymbolArg(symbol), Number(twapOrderId));
|
|
1556
1559
|
}
|
|
1557
1560
|
else {
|
|
1558
1561
|
errorAndExit("Cancel TWAP requires --exchange pacifica or hyperliquid");
|
|
@@ -1561,6 +1564,8 @@ export function registerTradeCommands(program, getAdapter, isJson, isDryRun = ()
|
|
|
1561
1564
|
return printJson(jsonOk(result));
|
|
1562
1565
|
console.log(chalk.green(`\n TWAP order ${twapOrderId} cancelled on ${adapter.name}.\n`));
|
|
1563
1566
|
});
|
|
1567
|
+
// === Pacifica-only order types (trade atomic) ===
|
|
1568
|
+
registerPacificaOrderCommands(trade, getAdapter, isJson, isDryRun);
|
|
1564
1569
|
// === Hyperliquid-only commands ===
|
|
1565
1570
|
supportsDryRun(trade.command("leverage <symbol> <leverage>"))
|
|
1566
1571
|
.description("Set leverage for a symbol")
|
|
@@ -1574,22 +1579,22 @@ export function registerTradeCommands(program, getAdapter, isJson, isDryRun = ()
|
|
|
1574
1579
|
const lev = Number(leverage.trim());
|
|
1575
1580
|
const adapter = await getAdapter();
|
|
1576
1581
|
const mode = opts.isolated ? "isolated" : "cross";
|
|
1577
|
-
if (dryRunGuard("set_leverage", { exchange: adapter.name, symbol: symbol
|
|
1582
|
+
if (dryRunGuard("set_leverage", { exchange: adapter.name, symbol: normalizeSymbolArg(symbol), leverage: lev, mode }))
|
|
1578
1583
|
return;
|
|
1579
1584
|
try {
|
|
1580
|
-
const result = await adapter.setLeverage(symbol
|
|
1585
|
+
const result = await adapter.setLeverage(normalizeSymbolArg(symbol), lev, mode);
|
|
1581
1586
|
logExecution({
|
|
1582
|
-
type: "rebalance", exchange: adapter.name, symbol: symbol
|
|
1587
|
+
type: "rebalance", exchange: adapter.name, symbol: normalizeSymbolArg(symbol), side: mode,
|
|
1583
1588
|
size: leverage, status: "success", dryRun: false,
|
|
1584
1589
|
meta: { action: "set_leverage", leverage: lev, mode },
|
|
1585
1590
|
});
|
|
1586
1591
|
if (isJson())
|
|
1587
1592
|
return printJson(jsonOk(result));
|
|
1588
|
-
console.log(chalk.green(`\n Leverage set to ${leverage}x (${mode}) for ${symbol
|
|
1593
|
+
console.log(chalk.green(`\n Leverage set to ${leverage}x (${mode}) for ${normalizeSymbolArg(symbol)} on ${adapter.name}.\n`));
|
|
1589
1594
|
}
|
|
1590
1595
|
catch (err) {
|
|
1591
1596
|
logExecution({
|
|
1592
|
-
type: "rebalance", exchange: adapter.name, symbol: symbol
|
|
1597
|
+
type: "rebalance", exchange: adapter.name, symbol: normalizeSymbolArg(symbol), side: mode,
|
|
1593
1598
|
size: leverage, status: "failed", dryRun: false,
|
|
1594
1599
|
error: extractErrorMessage(err),
|
|
1595
1600
|
meta: { action: "set_leverage", leverage: lev, mode },
|
|
@@ -1598,7 +1603,7 @@ export function registerTradeCommands(program, getAdapter, isJson, isDryRun = ()
|
|
|
1598
1603
|
}
|
|
1599
1604
|
});
|
|
1600
1605
|
/**
|
|
1601
|
-
* One verified reduce-only close
|
|
1606
|
+
* One verified reduce-only close: closePerpVerified (placePerpVerified
|
|
1602
1607
|
* + one residual market close), so the reported `closed` is a measured / venue-final fill, never
|
|
1603
1608
|
* the requested size. Logged "success" only when fully verified. A residual or an unknown outcome
|
|
1604
1609
|
* quarantines the symbol (CLI path "trade-close"); a definite rejection propagates unchanged.
|
|
@@ -1723,7 +1728,7 @@ export function registerTradeCommands(program, getAdapter, isJson, isDryRun = ()
|
|
|
1723
1728
|
.option("--slippage-bps <n>", "Slippage bound in bps (default: settings defaultSlippageBps)")
|
|
1724
1729
|
.action(async (symbol, opts) => {
|
|
1725
1730
|
await withJsonErrors(isJson(), async () => {
|
|
1726
|
-
const sym = symbol
|
|
1731
|
+
const sym = normalizeSymbolArg(symbol);
|
|
1727
1732
|
const adapter = await getAdapter();
|
|
1728
1733
|
refuseGatedReduceOnly(adapter);
|
|
1729
1734
|
const slippage = resolveAdapterSlippage(adapter, parseSlippageBpsFlag(opts.slippageBps));
|
|
@@ -1805,7 +1810,7 @@ export function registerTradeCommands(program, getAdapter, isJson, isDryRun = ()
|
|
|
1805
1810
|
.option("--slippage-bps <n>", "Slippage bound in bps (default: settings defaultSlippageBps)")
|
|
1806
1811
|
.action(async (symbol, percent, opts) => {
|
|
1807
1812
|
await withJsonErrors(isJson(), async () => {
|
|
1808
|
-
const sym = symbol
|
|
1813
|
+
const sym = normalizeSymbolArg(symbol);
|
|
1809
1814
|
const pct = parseFloat(percent);
|
|
1810
1815
|
if (isNaN(pct) || pct <= 0 || pct > 100)
|
|
1811
1816
|
errorAndExit("Percent must be between 0 and 100");
|
|
@@ -1864,7 +1869,7 @@ export function registerTradeCommands(program, getAdapter, isJson, isDryRun = ()
|
|
|
1864
1869
|
remediation: `Pass --symbol <SYM>: perp trade status ${orderId} --symbol <SYM> -e ${adapter.venue.id}`,
|
|
1865
1870
|
});
|
|
1866
1871
|
}
|
|
1867
|
-
const r = await adapter.getOrderStatus(opts.symbol
|
|
1872
|
+
const r = await adapter.getOrderStatus(normalizeSymbolArg(opts.symbol), parseOrderRef(orderId));
|
|
1868
1873
|
if (isJson())
|
|
1869
1874
|
return printJson(jsonOk(orderResultJson(r)));
|
|
1870
1875
|
printOrder(`Order ${orderId} (${r.symbol}) on ${adapter.name}`, r);
|
|
@@ -1885,7 +1890,7 @@ export function registerTradeCommands(program, getAdapter, isJson, isDryRun = ()
|
|
|
1885
1890
|
remediation: `Pass --symbol <SYM>: perp trade status ${orderId} --symbol <SYM> -e ${def.id}`,
|
|
1886
1891
|
});
|
|
1887
1892
|
}
|
|
1888
|
-
const r = await v2Of(adapter).getOrderStatus(opts.symbol !== undefined ? opts.symbol
|
|
1893
|
+
const r = await v2Of(adapter).getOrderStatus(opts.symbol !== undefined ? normalizeSymbolArg(opts.symbol) : "", ref);
|
|
1889
1894
|
if (isJson())
|
|
1890
1895
|
return printJson(jsonOk(orderResultJson(r)));
|
|
1891
1896
|
printOrder(`Order ${orderId} (${r.symbol}) on ${adapter.name}`, r);
|
|
@@ -1947,13 +1952,13 @@ export function registerTradeCommands(program, getAdapter, isJson, isDryRun = ()
|
|
|
1947
1952
|
const trades = await adapter.getTradeHistory(limit);
|
|
1948
1953
|
let filtered = trades;
|
|
1949
1954
|
if (symbol) {
|
|
1950
|
-
const sym = symbol
|
|
1955
|
+
const sym = normalizeSymbolArg(symbol);
|
|
1951
1956
|
filtered = trades.filter(t => symbolMatch(t.symbol, sym));
|
|
1952
1957
|
}
|
|
1953
1958
|
if (isJson())
|
|
1954
1959
|
return printJson(jsonOk(filtered));
|
|
1955
1960
|
if (filtered.length === 0) {
|
|
1956
|
-
console.log(chalk.gray(`\n No fills${symbol ? ` for ${symbol
|
|
1961
|
+
console.log(chalk.gray(`\n No fills${symbol ? ` for ${normalizeSymbolArg(symbol)}` : ""}.\n`));
|
|
1957
1962
|
return;
|
|
1958
1963
|
}
|
|
1959
1964
|
const rows = filtered.map((t) => [
|
|
@@ -1982,7 +1987,7 @@ export function registerTradeCommands(program, getAdapter, isJson, isDryRun = ()
|
|
|
1982
1987
|
errorAndExit("Side must be buy or sell");
|
|
1983
1988
|
const adapter = await getAdapter();
|
|
1984
1989
|
const validation = await validateTrade(adapter, {
|
|
1985
|
-
symbol: symbol
|
|
1990
|
+
symbol: normalizeSymbolArg(symbol),
|
|
1986
1991
|
side: s,
|
|
1987
1992
|
size: parseFloat(size),
|
|
1988
1993
|
price: opts.price ? parseFloat(opts.price) : undefined,
|
|
@@ -1992,7 +1997,7 @@ export function registerTradeCommands(program, getAdapter, isJson, isDryRun = ()
|
|
|
1992
1997
|
});
|
|
1993
1998
|
if (isJson())
|
|
1994
1999
|
return printJson(jsonOk(validation));
|
|
1995
|
-
console.log(chalk.cyan.bold(`\n Pre-Trade Check: ${symbol
|
|
2000
|
+
console.log(chalk.cyan.bold(`\n Pre-Trade Check: ${normalizeSymbolArg(symbol)} ${s.toUpperCase()} ${size}\n`));
|
|
1996
2001
|
for (const check of validation.checks) {
|
|
1997
2002
|
const icon = check.passed ? chalk.green("✓") : chalk.red("✗");
|
|
1998
2003
|
console.log(` ${icon} ${check.check}: ${check.message}`);
|
|
@@ -2023,7 +2028,7 @@ export function registerTradeCommands(program, getAdapter, isJson, isDryRun = ()
|
|
|
2023
2028
|
.option("--size <base>", "Total base amount (e.g., 0.01 BTC)")
|
|
2024
2029
|
.option("--force", "Bypass local risk-limit checks (maxPosition / total exposure)")
|
|
2025
2030
|
.action(async (symbol, side, opts) => {
|
|
2026
|
-
const sym = symbol
|
|
2031
|
+
const sym = normalizeSymbolArg(symbol);
|
|
2027
2032
|
const s = side.toLowerCase();
|
|
2028
2033
|
if (s !== "buy" && s !== "sell")
|
|
2029
2034
|
errorAndExit("Side must be buy or sell");
|
|
@@ -2174,7 +2179,7 @@ export function registerTradeCommands(program, getAdapter, isJson, isDryRun = ()
|
|
|
2174
2179
|
}
|
|
2175
2180
|
console.log(chalk.yellow(`\n PnL tracker stopped.\n`));
|
|
2176
2181
|
});
|
|
2177
|
-
// === Spot Trading (venue definitions
|
|
2182
|
+
// === Spot Trading (venue definitions) ===
|
|
2178
2183
|
/** Spot adapter for the current venue via the factory; CAPABILITY_UNSUPPORTED when def.spot is null. */
|
|
2179
2184
|
async function spotContext(confirmOnchainApprove) {
|
|
2180
2185
|
const adapter = await getAdapter();
|
|
@@ -2186,7 +2191,7 @@ export function registerTradeCommands(program, getAdapter, isJson, isDryRun = ()
|
|
|
2186
2191
|
const spot = await requireSpotAdapter(def.id, adapter, { confirmOnchainApprove });
|
|
2187
2192
|
return { def, spotDef, spot };
|
|
2188
2193
|
}
|
|
2189
|
-
/** Uniform feature name per spot capability
|
|
2194
|
+
/** Uniform feature name per spot capability. */
|
|
2190
2195
|
const SPOT_FEATURE = {
|
|
2191
2196
|
limitOrder: "spotLimitOrder", orderbook: "spotOrderbook", cancel: "spotCancel", transfer: "spotTransfer",
|
|
2192
2197
|
};
|
|
@@ -2255,7 +2260,7 @@ export function registerTradeCommands(program, getAdapter, isJson, isDryRun = ()
|
|
|
2255
2260
|
console.log();
|
|
2256
2261
|
}
|
|
2257
2262
|
/**
|
|
2258
|
-
* Execution record of a spot market order, same fields for every venue
|
|
2263
|
+
* Execution record of a spot market order, same fields for every venue:
|
|
2259
2264
|
* the resolved bound (bps + source) and, where the adapter reports it, the protection
|
|
2260
2265
|
* price actually sent and its reference (`protection` on the raw result / venueRaw).
|
|
2261
2266
|
*/
|
|
@@ -2332,7 +2337,7 @@ export function registerTradeCommands(program, getAdapter, isJson, isDryRun = ()
|
|
|
2332
2337
|
const slippageFlag = parseSlippageBpsFlag(opts.slippageBps);
|
|
2333
2338
|
const { def, spotDef, spot } = await spotContext(opts.yes === true);
|
|
2334
2339
|
const slippage = resolveSlippage(def.id, slippageFlag);
|
|
2335
|
-
const sym = symbol
|
|
2340
|
+
const sym = normalizeSymbolArg(symbol);
|
|
2336
2341
|
if (dryRunGuard(`spot ${verb}`, { exchange: def.id, symbol: sym, side, size }))
|
|
2337
2342
|
return;
|
|
2338
2343
|
const raw = await spot.spotMarketOrder(sym, side, size, { slippage });
|
|
@@ -2359,7 +2364,7 @@ export function registerTradeCommands(program, getAdapter, isJson, isDryRun = ()
|
|
|
2359
2364
|
throw new PerpError("INVALID_PARAMS", `Side must be buy or sell (got "${side}")`);
|
|
2360
2365
|
const { def, spotDef, spot } = await spotContext(false);
|
|
2361
2366
|
requireSpotCapability(def, spotDef, "limitOrder", "limit orders");
|
|
2362
|
-
const sym = symbol
|
|
2367
|
+
const sym = normalizeSymbolArg(symbol);
|
|
2363
2368
|
if (dryRunGuard("spot limit", { exchange: def.id, symbol: sym, side: s, price, size, tif: opts.tif ?? null }))
|
|
2364
2369
|
return;
|
|
2365
2370
|
const raw = await spot.spotLimitOrder(sym, s, price, size, opts.tif !== undefined ? { tif: opts.tif } : undefined);
|
|
@@ -2379,7 +2384,7 @@ export function registerTradeCommands(program, getAdapter, isJson, isDryRun = ()
|
|
|
2379
2384
|
await withJsonErrors(isJson(), async () => {
|
|
2380
2385
|
const { def, spotDef, spot } = await spotContext(false);
|
|
2381
2386
|
requireSpotCapability(def, spotDef, "cancel", "order cancel");
|
|
2382
|
-
const sym = symbol
|
|
2387
|
+
const sym = normalizeSymbolArg(symbol);
|
|
2383
2388
|
if (dryRunGuard("spot cancel", { exchange: def.id, symbol: sym, orderId }))
|
|
2384
2389
|
return;
|
|
2385
2390
|
const result = spotCancelResult(def, sym, orderId, await spot.spotCancelOrder(sym, orderId));
|
|
@@ -2401,7 +2406,7 @@ export function registerTradeCommands(program, getAdapter, isJson, isDryRun = ()
|
|
|
2401
2406
|
if (spotDef.accountModel === "wallet" || !isSpotOpenOrdersCapable(spot)) {
|
|
2402
2407
|
throw unsupported(def.displayName, "spotOpenOrders", spotDef.accountModel === "wallet" ? "spot is an on-chain swap that settles immediately — there are no resting orders" : "the spot adapter cannot list open orders", spotDef.accountModel === "wallet" ? `Check the wallet balance: perp -e ${def.id} trade spot-balance` : "Use the venue UI to inspect open spot orders");
|
|
2403
2408
|
}
|
|
2404
|
-
const orders = await spot.getSpotOpenOrders(symbol === undefined ? null : symbol
|
|
2409
|
+
const orders = await spot.getSpotOpenOrders(symbol === undefined ? null : normalizeSymbolArg(symbol));
|
|
2405
2410
|
if (isJson())
|
|
2406
2411
|
return printJson(jsonOk({ exchange: def.id, orders }));
|
|
2407
2412
|
console.log(chalk.cyan(`\n Open spot orders (${def.id}):\n`));
|
|
@@ -2520,7 +2525,7 @@ function parseMultiLeg(spec) {
|
|
|
2520
2525
|
if (isNaN(Number(size)) || Number(size) <= 0) {
|
|
2521
2526
|
throw new Error(`Invalid size "${size}" — must be a positive number`);
|
|
2522
2527
|
}
|
|
2523
|
-
return { exchange: normalizedExchange, symbol: symbol
|
|
2528
|
+
return { exchange: normalizedExchange, symbol: normalizeSymbolArg(symbol), side, size };
|
|
2524
2529
|
}
|
|
2525
2530
|
function registerMultiAction(parent, getAdapterForExchange, isJson, deprecated = false) {
|
|
2526
2531
|
const cmd = parent
|
package/dist/commands/wallet.js
CHANGED
|
@@ -525,7 +525,7 @@ export function registerWalletCommands(program, isJson, getAdapter, getPacificaA
|
|
|
525
525
|
const info = EXCHANGE_ENV_MAP[resolved];
|
|
526
526
|
if (!info) {
|
|
527
527
|
throw new PerpError("INVALID_PARAMS", `Unknown exchange: ${exchange}`, {
|
|
528
|
-
remediation: "Use: pacifica, hyperliquid, lighter, aster (or hl, pac, lt, ast). Lighter RH / Ondo / Arcus use: perp wallet agent approve <lighter-rh|ondo|arcus>; QFEX: perp wallet agent import qfex.",
|
|
528
|
+
remediation: "Use: pacifica, hyperliquid, lighter, aster (or hl, pac, lt, ast). Lighter RH / Ondo / Arcus use: perp wallet agent approve <lighter-rh|ondo|arcus>; QFEX: perp wallet agent import qfex. TxFlow, Variational and Extended are market data only; RISEx is read-only in this release (trading later).",
|
|
529
529
|
});
|
|
530
530
|
}
|
|
531
531
|
const { promptHidden } = await import("./agent.js");
|
package/dist/config.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Keypair } from "@solana/web3.js";
|
|
2
2
|
import type { Network, VenueId } from "./exchanges/venue-types.js";
|
|
3
3
|
import type { VenueCredentials } from "./exchanges/venue.js";
|
|
4
|
-
/** @deprecated alias — every venue id is an exchange id
|
|
4
|
+
/** @deprecated alias — every venue id is an exchange id. */
|
|
5
5
|
export type Exchange = VenueId;
|
|
6
6
|
/** CLI (and MCP) inputs that select credentials. Absent flag = null. */
|
|
7
7
|
export interface CliCredentialOpts {
|
|
@@ -27,7 +27,7 @@ export declare function resolveOwsWallet(walletName: string): string | null;
|
|
|
27
27
|
export declare function isOwsKey(key: string): boolean;
|
|
28
28
|
/** Extract OWS wallet name from an ows: prefixed key string. */
|
|
29
29
|
export declare function getOwsWalletName(key: string): string;
|
|
30
|
-
/** CLI/MCP-shared credential resolution
|
|
30
|
+
/** CLI/MCP-shared credential resolution. */
|
|
31
31
|
export declare function resolveCredentials(venue: VenueId, opts: CliCredentialOpts, network: Network): Promise<VenueCredentials>;
|
|
32
32
|
/**
|
|
33
33
|
* Whether any credential source is configured for the venue (explicit checks,
|
package/dist/config.js
CHANGED
|
@@ -8,7 +8,7 @@ import { PerpError } from "./errors.js";
|
|
|
8
8
|
import { isLegacyVenueId } from "./exchanges/venue-types.js";
|
|
9
9
|
const _require = createRequire(import.meta.url);
|
|
10
10
|
/**
|
|
11
|
-
* Legacy env private-key variables
|
|
11
|
+
* Legacy env private-key variables. New venues have no env tier:
|
|
12
12
|
* loadPrivateKey()/resolveCredentials() never read env keys for them.
|
|
13
13
|
* v0.12.10 restored ASTER_PRIVATE_KEY so Aster Tier 3 env-PK loading works.
|
|
14
14
|
*/
|
|
@@ -50,7 +50,7 @@ function envKeyOf(venue) {
|
|
|
50
50
|
if (v)
|
|
51
51
|
return v;
|
|
52
52
|
}
|
|
53
|
-
// config.ts generic PRIVATE_KEY —
|
|
53
|
+
// config.ts generic PRIVATE_KEY — out of scope (legacy venues only).
|
|
54
54
|
const generic = process.env.PRIVATE_KEY;
|
|
55
55
|
return generic ? generic : null;
|
|
56
56
|
}
|
|
@@ -106,6 +106,12 @@ async function agentOf(venue) {
|
|
|
106
106
|
case "ondo": return store.getVenueAgent("ondo");
|
|
107
107
|
case "arcus": return store.getVenueAgent("arcus");
|
|
108
108
|
case "qfex": return store.getVenueAgent("qfex");
|
|
109
|
+
case "risex": return store.getVenueAgent("risex");
|
|
110
|
+
// Market-data-only venues: no agent.
|
|
111
|
+
case "txflow": return null;
|
|
112
|
+
case "variational": return null;
|
|
113
|
+
case "grvt": return null;
|
|
114
|
+
case "extended": return null;
|
|
109
115
|
case "pacifica":
|
|
110
116
|
case "hyperliquid":
|
|
111
117
|
case "lighter":
|
|
@@ -125,6 +131,11 @@ async function agentForNetwork(venue, network) {
|
|
|
125
131
|
case "ondo": return store.getVenueAgentForNetwork("ondo", network);
|
|
126
132
|
case "arcus": return store.getVenueAgentForNetwork("arcus", network);
|
|
127
133
|
case "qfex": return store.getVenueAgentForNetwork("qfex", network);
|
|
134
|
+
case "risex": return store.getVenueAgentForNetwork("risex", network);
|
|
135
|
+
case "txflow": return null;
|
|
136
|
+
case "variational": return null;
|
|
137
|
+
case "grvt": return null;
|
|
138
|
+
case "extended": return null;
|
|
128
139
|
case "pacifica":
|
|
129
140
|
case "hyperliquid":
|
|
130
141
|
case "lighter":
|
|
@@ -151,11 +162,11 @@ function owsPassphraseOf(opts) {
|
|
|
151
162
|
function owsKeyTokenOf(opts) {
|
|
152
163
|
if (opts.owsKey !== null)
|
|
153
164
|
return opts.owsKey;
|
|
154
|
-
// OWS_API_KEY env: recorded as out of scope in
|
|
165
|
+
// OWS_API_KEY env: recorded as out of scope in (kept as before).
|
|
155
166
|
const env = process.env.OWS_API_KEY;
|
|
156
167
|
return env ? env : null;
|
|
157
168
|
}
|
|
158
|
-
/** CLI/MCP-shared credential resolution
|
|
169
|
+
/** CLI/MCP-shared credential resolution. */
|
|
159
170
|
export async function resolveCredentials(venue, opts, network) {
|
|
160
171
|
const withToken = { ...opts, owsKey: owsKeyTokenOf(opts) };
|
|
161
172
|
// Custodial venues (masterKeyKind "none": qfex) have no master key to resolve.
|
package/dist/constants.js
CHANGED
|
@@ -28,6 +28,11 @@ export const TAKER_FEES = {
|
|
|
28
28
|
// orders (0.05%) are never under-estimated. Lower tiers / the lifetime-tier promotion: set
|
|
29
29
|
// settings.fees.qfex.
|
|
30
30
|
qfex: 0.001,
|
|
31
|
+
// RISEx Tier 1 ($0 rolling 14-day volume; docs.risechain.com/docs/risex/trading/fees, read
|
|
32
|
+
// 2026-10-14): taker 3.00 bps, maker 1.00 bps; higher tiers down to 1.50 / 0.00 bps — set
|
|
33
|
+
// settings.fees.risex for a higher tier.
|
|
34
|
+
risex: 0.0003,
|
|
35
|
+
// TxFlow: no trading in perp-cli (market data only) — no fee entry on purpose.
|
|
31
36
|
};
|
|
32
37
|
/**
|
|
33
38
|
* Published maker fee per venue (fraction; negative = rebate). Only venues whose base-tier
|
|
@@ -41,6 +46,7 @@ export const MAKER_FEES = {
|
|
|
41
46
|
aster: 0,
|
|
42
47
|
"lighter-rh": 0, // Standard tier (Premium: 0.012% under $1M 14-day volume)
|
|
43
48
|
qfex: 0.0005, // Tier 5 single stocks 0.05% (indices / commodities 0.02%); see TAKER_FEES.qfex
|
|
49
|
+
risex: 0.0001, // Tier 1 maker 1.00 bps; see TAKER_FEES.risex
|
|
44
50
|
};
|
|
45
51
|
/**
|
|
46
52
|
* Taker fee for a venue: settings.fees[venue].taker → TAKER_FEES[venue] → DEFAULT_TAKER_FEE
|
package/dist/dashboard/server.js
CHANGED
|
@@ -100,7 +100,7 @@ async function pollArbData() {
|
|
|
100
100
|
const row = {
|
|
101
101
|
base,
|
|
102
102
|
dexes: assets.map((a) => {
|
|
103
|
-
// Venue ref: "hyperliquid" for native, "hl:<dex>" for HIP-3 (same basis
|
|
103
|
+
// Venue ref: "hyperliquid" for native, "hl:<dex>" for HIP-3 (same basis)
|
|
104
104
|
const ann = annualizeRate(a.fundingRate, a.dex === "hl" ? "hyperliquid" : `hl:${a.dex}`);
|
|
105
105
|
return { dex: a.dex, rate: a.fundingRate, annualizedPct: ann, markPrice: a.markPrice, oi: a.openInterest };
|
|
106
106
|
}),
|
package/dist/dashboard/ui.js
CHANGED
|
@@ -334,7 +334,7 @@ function renderPanels(exchanges) {
|
|
|
334
334
|
return \`<tr>\${isHL?\`<td style="color:var(--purple);font-size:11px">\${dex}</td>\`:''}<td>\${sym}</td><td class="side-\${o.side}">\${o.side.toUpperCase()}</td><td>\${o.type}</td><td>$\${fmt(o.price)}</td><td>\${o.size}</td><td>\${o.filled}</td><td>\${o.status}</td></tr>\`;
|
|
335
335
|
}).join('')}</tbody></table>\`:(ordKnown?'<div class="empty-msg">No open orders</div>':'<div class="empty-msg">Open orders '+NA+'</div>')}
|
|
336
336
|
<div class="section-title">Markets (Top 10)</div>
|
|
337
|
-
\${ex.topMarkets.length?\`<table><thead><tr><th>Symbol</th><th>Mark</th><th>Index</th><th>Funding</th><th>24h Vol</th><th>OI</th><th>Max Lev</th></tr></thead><tbody>\${ex.topMarkets.map(m=>{const fr=Number(m.fundingRate);return\`<tr><td>\${m.symbol}</td><td>$\${fmt(m.markPrice)}</td><td>\${usdOrDash(m.indexPrice)}</td><td class="\${rateClass(fr)}">\${(fr*100).toFixed(4)}%</td><td>\${usdOrDash(m.volume24h,0)}</td><td>\${usdOrDash(m.openInterest,0)}</td><td>\${m.maxLeverage}
|
|
337
|
+
\${ex.topMarkets.length?\`<table><thead><tr><th>Symbol</th><th>Mark</th><th>Index</th><th>Funding</th><th>24h Vol</th><th>OI</th><th>Max Lev</th></tr></thead><tbody>\${ex.topMarkets.map(m=>{const fr=Number(m.fundingRate);return\`<tr><td>\${m.symbol}</td><td>$\${fmt(m.markPrice)}</td><td>\${usdOrDash(m.indexPrice)}</td><td class="\${rateClass(fr)}">\${(fr*100).toFixed(4)}%</td><td>\${usdOrDash(m.volume24h,0)}</td><td>\${usdOrDash(m.openInterest,0)}</td><td>\${m.maxLeverage==null?'-':m.maxLeverage+'x'}</td></tr>\`;}).join('')}</tbody></table>\`:'<div class="empty-msg">No market data</div>'}
|
|
338
338
|
</div>\`;
|
|
339
339
|
}).join('');
|
|
340
340
|
|