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
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* RISEx unit conversions — all exact (BigInt / string), never float maths.
|
|
3
|
+
*
|
|
4
|
+
* - WAD: on-chain style 1e18-scaled integers (`/v1/positions` size / prices, IOC
|
|
5
|
+
* `filled_quantity`) → plain decimal strings.
|
|
6
|
+
* - ns: every RISEx timestamp and `funding_interval` is in NANOseconds → ms.
|
|
7
|
+
* - steps / ticks: orders are sent as integers — size_steps = size / step_size (uint32),
|
|
8
|
+
* price_ticks = price / step_price (uint24, max 16 777 215). Off-grid values are refused,
|
|
9
|
+
* never rounded.
|
|
10
|
+
*/
|
|
11
|
+
export declare const UINT24_MAX = 16777215n;
|
|
12
|
+
export declare const UINT32_MAX = 4294967295n;
|
|
13
|
+
export declare const UINT64_MAX = 18446744073709551615n;
|
|
14
|
+
/** 1e18-scaled integer string ("1143307000000000000", "-2905…") → decimal string ("1.143307"). */
|
|
15
|
+
export declare function wadToDecimal(wad: string): string;
|
|
16
|
+
/** Nanosecond decimal string → unix ms (integer division; exact for ms-aligned values). */
|
|
17
|
+
export declare function nsToMs(ns: string | number): number;
|
|
18
|
+
/** ns timestamp or null for "0" / "" / absent (RISEx sends "0" for "no value"). */
|
|
19
|
+
export declare function nsToMsOrNull(ns: unknown): number | null;
|
|
20
|
+
/** Integer size in steps; refused when off the step grid, zero, or above uint32. */
|
|
21
|
+
export declare function toSizeSteps(size: string, stepSize: string): bigint;
|
|
22
|
+
/** Integer price in ticks; refused (INVALID_TICK) when off-grid, INVALID_PARAMS when above uint24. */
|
|
23
|
+
export declare function toPriceTicks(price: string, stepPrice: string): bigint;
|
|
24
|
+
/** A RISEx decimal field, or null when absent / empty (RISEx sends "" for "not set"). */
|
|
25
|
+
export declare function decOrNull(v: unknown): string | null;
|
|
26
|
+
/** Exact comparison of two plain decimal strings: -1 | 0 | 1. */
|
|
27
|
+
export declare function compareDecimal(a: string, b: string): -1 | 0 | 1;
|
|
28
|
+
/** Strip trailing zeros of a plain decimal ("83425.5900" → "83425.59"). */
|
|
29
|
+
export declare function trimDecimal(s: string): string;
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* RISEx unit conversions — all exact (BigInt / string), never float maths.
|
|
3
|
+
*
|
|
4
|
+
* - WAD: on-chain style 1e18-scaled integers (`/v1/positions` size / prices, IOC
|
|
5
|
+
* `filled_quantity`) → plain decimal strings.
|
|
6
|
+
* - ns: every RISEx timestamp and `funding_interval` is in NANOseconds → ms.
|
|
7
|
+
* - steps / ticks: orders are sent as integers — size_steps = size / step_size (uint32),
|
|
8
|
+
* price_ticks = price / step_price (uint24, max 16 777 215). Off-grid values are refused,
|
|
9
|
+
* never rounded.
|
|
10
|
+
*/
|
|
11
|
+
import { PerpError } from "../../errors.js";
|
|
12
|
+
import { exactUnits } from "../precision.js";
|
|
13
|
+
export const UINT24_MAX = 16777215n;
|
|
14
|
+
export const UINT32_MAX = 4294967295n;
|
|
15
|
+
export const UINT64_MAX = 18446744073709551615n;
|
|
16
|
+
/** 1e18-scaled integer string ("1143307000000000000", "-2905…") → decimal string ("1.143307"). */
|
|
17
|
+
export function wadToDecimal(wad) {
|
|
18
|
+
const s = wad.trim();
|
|
19
|
+
if (!/^-?\d+$/.test(s))
|
|
20
|
+
throw new PerpError("EXCHANGE_ERROR", `RISEx: "${wad}" is not a WAD integer`, { exchange: "risex" });
|
|
21
|
+
const neg = s.startsWith("-");
|
|
22
|
+
const digits = (neg ? s.slice(1) : s).replace(/^0+(?=\d)/, "");
|
|
23
|
+
const padded = digits.padStart(19, "0");
|
|
24
|
+
const int = padded.slice(0, padded.length - 18).replace(/^0+(?=\d)/, "");
|
|
25
|
+
const frac = padded.slice(padded.length - 18).replace(/0+$/, "");
|
|
26
|
+
const out = frac.length > 0 ? `${int}.${frac}` : int;
|
|
27
|
+
return neg && out !== "0" ? `-${out}` : out;
|
|
28
|
+
}
|
|
29
|
+
/** Nanosecond decimal string → unix ms (integer division; exact for ms-aligned values). */
|
|
30
|
+
export function nsToMs(ns) {
|
|
31
|
+
const s = String(ns).trim();
|
|
32
|
+
if (!/^\d+$/.test(s))
|
|
33
|
+
throw new PerpError("EXCHANGE_ERROR", `RISEx: "${ns}" is not a nanosecond timestamp`, { exchange: "risex" });
|
|
34
|
+
return Number(BigInt(s) / 1000000n);
|
|
35
|
+
}
|
|
36
|
+
/** ns timestamp or null for "0" / "" / absent (RISEx sends "0" for "no value"). */
|
|
37
|
+
export function nsToMsOrNull(ns) {
|
|
38
|
+
if (typeof ns !== "string" && typeof ns !== "number")
|
|
39
|
+
return null;
|
|
40
|
+
const s = String(ns).trim();
|
|
41
|
+
if (s === "" || s === "0")
|
|
42
|
+
return null;
|
|
43
|
+
return nsToMs(s);
|
|
44
|
+
}
|
|
45
|
+
/** Integer size in steps; refused when off the step grid, zero, or above uint32. */
|
|
46
|
+
export function toSizeSteps(size, stepSize) {
|
|
47
|
+
const u = exactUnits(size, stepSize);
|
|
48
|
+
if (u === null) {
|
|
49
|
+
throw new PerpError("INVALID_PARAMS", `RISEx size ${size} is not a multiple of the step size ${stepSize}`, {
|
|
50
|
+
exchange: "risex", remediation: `Use a size that is a multiple of ${stepSize}`,
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
if (u <= 0n)
|
|
54
|
+
throw new PerpError("SIZE_TOO_SMALL", `RISEx size ${size} is below one step (${stepSize})`, { exchange: "risex" });
|
|
55
|
+
if (u > UINT32_MAX) {
|
|
56
|
+
throw new PerpError("SIZE_TOO_LARGE", `RISEx size ${size} is ${u} steps, above the uint32 limit`, {
|
|
57
|
+
exchange: "risex", remediation: "Split the order",
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
return u;
|
|
61
|
+
}
|
|
62
|
+
/** Integer price in ticks; refused (INVALID_TICK) when off-grid, INVALID_PARAMS when above uint24. */
|
|
63
|
+
export function toPriceTicks(price, stepPrice) {
|
|
64
|
+
const u = exactUnits(price, stepPrice);
|
|
65
|
+
if (u === null) {
|
|
66
|
+
throw new PerpError("INVALID_TICK", `RISEx price ${price} is not a multiple of the tick ${stepPrice}`, {
|
|
67
|
+
exchange: "risex", remediation: `Use a price that is a multiple of ${stepPrice}`,
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
if (u <= 0n)
|
|
71
|
+
throw new PerpError("INVALID_PARAMS", `RISEx price ${price} must be positive`, { exchange: "risex" });
|
|
72
|
+
if (u > UINT24_MAX) {
|
|
73
|
+
throw new PerpError("INVALID_PARAMS", `RISEx price ${price} is ${u} ticks, above the uint24 order limit (${UINT24_MAX})`, { exchange: "risex" });
|
|
74
|
+
}
|
|
75
|
+
return u;
|
|
76
|
+
}
|
|
77
|
+
/** A RISEx decimal field, or null when absent / empty (RISEx sends "" for "not set"). */
|
|
78
|
+
export function decOrNull(v) {
|
|
79
|
+
if (typeof v !== "string")
|
|
80
|
+
return null;
|
|
81
|
+
const s = v.trim();
|
|
82
|
+
return /^-?\d+(\.\d+)?$/.test(s) ? s : null;
|
|
83
|
+
}
|
|
84
|
+
/** Exact comparison of two plain decimal strings: -1 | 0 | 1. */
|
|
85
|
+
export function compareDecimal(a, b) {
|
|
86
|
+
const parse = (s) => {
|
|
87
|
+
if (!/^-?\d+(\.\d+)?$/.test(s))
|
|
88
|
+
throw new PerpError("EXCHANGE_ERROR", `RISEx: "${s}" is not a decimal`, { exchange: "risex" });
|
|
89
|
+
const neg = s.startsWith("-");
|
|
90
|
+
const [ip, fp = ""] = (neg ? s.slice(1) : s).split(".");
|
|
91
|
+
return { int: BigInt(ip + fp) * (neg ? -1n : 1n), scale: fp.length };
|
|
92
|
+
};
|
|
93
|
+
const x = parse(a), y = parse(b);
|
|
94
|
+
const scale = Math.max(x.scale, y.scale);
|
|
95
|
+
const xi = x.int * 10n ** BigInt(scale - x.scale), yi = y.int * 10n ** BigInt(scale - y.scale);
|
|
96
|
+
return xi < yi ? -1 : xi > yi ? 1 : 0;
|
|
97
|
+
}
|
|
98
|
+
/** Strip trailing zeros of a plain decimal ("83425.5900" → "83425.59"). */
|
|
99
|
+
export function trimDecimal(s) {
|
|
100
|
+
if (!s.includes("."))
|
|
101
|
+
return s;
|
|
102
|
+
const out = s.replace(/0+$/, "").replace(/\.$/, "");
|
|
103
|
+
return out === "-0" ? "0" : out;
|
|
104
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Single slippage resolver for every market-type order
|
|
2
|
+
* Single slippage resolver for every market-type order.
|
|
3
3
|
*
|
|
4
4
|
* Precedence (all explicit configuration, no hidden constants):
|
|
5
5
|
* --slippage-bps flag → settings.slippageBps[venue] → settings.defaultSlippageBps.
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Single slippage resolver for every market-type order
|
|
2
|
+
* Single slippage resolver for every market-type order.
|
|
3
3
|
*
|
|
4
4
|
* Precedence (all explicit configuration, no hidden constants):
|
|
5
5
|
* --slippage-bps flag → settings.slippageBps[venue] → settings.defaultSlippageBps.
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Spot adapter factory — the only place spot adapters are created
|
|
2
|
+
* Spot adapter factory — the only place spot adapters are created.
|
|
3
3
|
*
|
|
4
4
|
* - `getSpotAdapter` returns null iff the venue has no spot (`def.spot === null`).
|
|
5
5
|
* Creation / init / network errors propagate (SSOT Rule #2 — no catch).
|
|
@@ -8,8 +8,8 @@
|
|
|
8
8
|
* so the key is the adapter's own network, not the process-wide one) and per
|
|
9
9
|
* on-chain-approve option. Read-only and signed perp adapters keep separate entries.
|
|
10
10
|
*
|
|
11
|
-
* Bot scope
|
|
12
|
-
* Quote/base detection
|
|
11
|
+
* Bot scope: bots only trade spot on venues where `isBotSpotVenue(def)`.
|
|
12
|
+
* Quote/base detection goes through `def.spot.quoteAssets`.
|
|
13
13
|
*/
|
|
14
14
|
import type { AssetClass, Network, VenueMarket } from "./venue-types.js";
|
|
15
15
|
import type { ExchangeAdapter } from "./interface.js";
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Spot adapter factory — the only place spot adapters are created
|
|
2
|
+
* Spot adapter factory — the only place spot adapters are created.
|
|
3
3
|
*
|
|
4
4
|
* - `getSpotAdapter` returns null iff the venue has no spot (`def.spot === null`).
|
|
5
5
|
* Creation / init / network errors propagate (SSOT Rule #2 — no catch).
|
|
@@ -8,8 +8,8 @@
|
|
|
8
8
|
* so the key is the adapter's own network, not the process-wide one) and per
|
|
9
9
|
* on-chain-approve option. Read-only and signed perp adapters keep separate entries.
|
|
10
10
|
*
|
|
11
|
-
* Bot scope
|
|
12
|
-
* Quote/base detection
|
|
11
|
+
* Bot scope: bots only trade spot on venues where `isBotSpotVenue(def)`.
|
|
12
|
+
* Quote/base detection goes through `def.spot.quoteAssets`.
|
|
13
13
|
*/
|
|
14
14
|
import { classifyError, extractErrorMessage, PerpError } from "../errors.js";
|
|
15
15
|
import { unsupported } from "./trading-v2.js";
|
|
@@ -84,7 +84,7 @@ export async function requireSpotAdapter(venue, perp, opts = NO_ONCHAIN_APPROVE)
|
|
|
84
84
|
throw new Error(`unreachable: ${venue} spot definition present but no adapter`);
|
|
85
85
|
return spot;
|
|
86
86
|
}
|
|
87
|
-
// ── Bot scope
|
|
87
|
+
// ── Bot scope ──
|
|
88
88
|
/**
|
|
89
89
|
* Bots (funding-arb*, spot-perp-*, arb-auto, arb/index) only use spot venues with a
|
|
90
90
|
* separate (or runtime-decided) spot balance and a spot↔perp transfer. Pacifica
|
|
@@ -116,7 +116,7 @@ export async function getBotSpotAdapter(name, perp) {
|
|
|
116
116
|
return null;
|
|
117
117
|
return getSpotAdapter(id, perp);
|
|
118
118
|
}
|
|
119
|
-
// ── Quote / base helpers
|
|
119
|
+
// ── Quote / base helpers ──
|
|
120
120
|
/** Upper-cased token with venue balance suffixes removed ("USDC_SPOT" / "PURR-SPOT" → base). */
|
|
121
121
|
export function spotTokenBase(token) {
|
|
122
122
|
return token.toUpperCase().replace(/[-_]SPOT$/, "");
|
|
@@ -171,7 +171,7 @@ export function spotSharesPerpMargin(def, perp) {
|
|
|
171
171
|
* `shared` is carried through so aggregators can skip money already in perp equity.
|
|
172
172
|
*/
|
|
173
173
|
export function valueSpotBalances(def, net, balances, markets, warn) {
|
|
174
|
-
// markPrice null = no usable price (
|
|
174
|
+
// markPrice null = no usable price (SpotMarketInfo) → the token is excluded below.
|
|
175
175
|
const prices = new Map(markets.flatMap(m => (m.markPrice === null ? [] : [[m.baseToken.toUpperCase(), Number(m.markPrice)]])));
|
|
176
176
|
const out = [];
|
|
177
177
|
for (const b of balances) {
|
|
@@ -248,7 +248,7 @@ export async function withSpotTimeout(p, what, timeoutMs = SPOT_VALUATION_TIMEOU
|
|
|
248
248
|
clearTimeout(timer);
|
|
249
249
|
}
|
|
250
250
|
}
|
|
251
|
-
// ── Transfers
|
|
251
|
+
// ── Transfers ──
|
|
252
252
|
function ownerOf(spot) {
|
|
253
253
|
const o = _owner.get(spot);
|
|
254
254
|
if (!o)
|
|
@@ -3,10 +3,10 @@
|
|
|
3
3
|
* Separate from ExchangeAdapter because spot uses balance-based tracking
|
|
4
4
|
* (not position-based) and has no leverage, liquidation, or funding concepts.
|
|
5
5
|
*
|
|
6
|
-
* Venues declare spot support in their VenueDefinition (`spot
|
|
6
|
+
* Venues declare spot support in their VenueDefinition (`spot`);
|
|
7
7
|
* adapters are created only through `getSpotAdapter()` in spot-factory.ts.
|
|
8
8
|
*
|
|
9
|
-
* Symbol rule
|
|
9
|
+
* Symbol rule: `SpotMarketInfo.symbol` is "BASE/QUOTE". Adapter
|
|
10
10
|
* methods accept a bare base ("ETH" → BASE/quoteAssets[0]) or "BASE/QUOTE"
|
|
11
11
|
* and translate to the venue's wire form.
|
|
12
12
|
*/
|
|
@@ -33,11 +33,11 @@ export interface SpotBalance {
|
|
|
33
33
|
entryNtl?: string;
|
|
34
34
|
/**
|
|
35
35
|
* true = this balance is the same money as the perp account (Pacifica USDC,
|
|
36
|
-
* HL unified). Aggregators must not add it on top of perp equity
|
|
36
|
+
* HL unified). Aggregators must not add it on top of perp equity.
|
|
37
37
|
*/
|
|
38
38
|
shared: boolean;
|
|
39
39
|
}
|
|
40
|
-
/** Normalized spot order result for
|
|
40
|
+
/** Normalized spot order result for venue-definition spot venues (legacy HL/Lighter return unknown). */
|
|
41
41
|
export interface SpotOrderResult {
|
|
42
42
|
venue: string;
|
|
43
43
|
symbol: string;
|
|
@@ -129,7 +129,7 @@ export interface SpotAdapter {
|
|
|
129
129
|
asks: [string, string][];
|
|
130
130
|
}>;
|
|
131
131
|
getSpotBalances(): Promise<SpotBalance[]>;
|
|
132
|
-
/** Market order within the resolved slippage bound (
|
|
132
|
+
/** Market order within the resolved slippage bound (same rule as perps). */
|
|
133
133
|
spotMarketOrder(symbol: string, side: "buy" | "sell", size: string, opts: {
|
|
134
134
|
slippage: ResolvedSlippage;
|
|
135
135
|
}): Promise<unknown>;
|
|
@@ -3,10 +3,10 @@
|
|
|
3
3
|
* Separate from ExchangeAdapter because spot uses balance-based tracking
|
|
4
4
|
* (not position-based) and has no leverage, liquidation, or funding concepts.
|
|
5
5
|
*
|
|
6
|
-
* Venues declare spot support in their VenueDefinition (`spot
|
|
6
|
+
* Venues declare spot support in their VenueDefinition (`spot`);
|
|
7
7
|
* adapters are created only through `getSpotAdapter()` in spot-factory.ts.
|
|
8
8
|
*
|
|
9
|
-
* Symbol rule
|
|
9
|
+
* Symbol rule: `SpotMarketInfo.symbol` is "BASE/QUOTE". Adapter
|
|
10
10
|
* methods accept a bare base ("ETH" → BASE/quoteAssets[0]) or "BASE/QUOTE"
|
|
11
11
|
* and translate to the venue's wire form.
|
|
12
12
|
*/
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Spot order outcome → filled size, for bots / arb
|
|
2
|
+
* Spot order outcome → filled size, for bots / arb.
|
|
3
3
|
*
|
|
4
|
-
* A resolved `spotMarketOrder()` promise does NOT mean "filled": the
|
|
4
|
+
* A resolved `spotMarketOrder()` promise does NOT mean "filled": the adapters
|
|
5
5
|
* return a `SpotOrderResult` (rejected / cancelled / pending / partial / filled),
|
|
6
6
|
* and the legacy Hyperliquid / Lighter adapters return a venue payload. Every bot /
|
|
7
7
|
* arb spot leg goes through `spotMarketFill()` so the hedge is sized to what
|
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
* zero delta is ORDER_PENDING (the IOC may still be unconfirmed), never "nothing
|
|
32
32
|
* filled"; a failed read is never zero.
|
|
33
33
|
*
|
|
34
|
-
* Venue-final IOC outcome
|
|
34
|
+
* Venue-final IOC outcome: when the spot adapter is IocOutcomeCapable
|
|
35
35
|
* and iocOutcomeOf(result) reports the order's FINAL filled size, that report is used in
|
|
36
36
|
* preference to the balance delta, cross-checked against a fresh base-balance delta:
|
|
37
37
|
* - final 0 and the balance did not move (≤ one lot) → ORDER_REJECTED (definite);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Spot order outcome → filled size, for bots / arb
|
|
2
|
+
* Spot order outcome → filled size, for bots / arb.
|
|
3
3
|
*
|
|
4
|
-
* A resolved `spotMarketOrder()` promise does NOT mean "filled": the
|
|
4
|
+
* A resolved `spotMarketOrder()` promise does NOT mean "filled": the adapters
|
|
5
5
|
* return a `SpotOrderResult` (rejected / cancelled / pending / partial / filled),
|
|
6
6
|
* and the legacy Hyperliquid / Lighter adapters return a venue payload. Every bot /
|
|
7
7
|
* arb spot leg goes through `spotMarketFill()` so the hedge is sized to what
|
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
* zero delta is ORDER_PENDING (the IOC may still be unconfirmed), never "nothing
|
|
32
32
|
* filled"; a failed read is never zero.
|
|
33
33
|
*
|
|
34
|
-
* Venue-final IOC outcome
|
|
34
|
+
* Venue-final IOC outcome: when the spot adapter is IocOutcomeCapable
|
|
35
35
|
* and iocOutcomeOf(result) reports the order's FINAL filled size, that report is used in
|
|
36
36
|
* preference to the balance delta, cross-checked against a fresh base-balance delta:
|
|
37
37
|
* - final 0 and the balance did not move (≤ one lot) → ORDER_REJECTED (definite);
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* TradingV2 — normalized order entry for venues added under the venue
|
|
3
|
-
* abstraction
|
|
3
|
+
* abstraction.
|
|
4
4
|
*
|
|
5
5
|
* Method names deliberately do not overlap the legacy ExchangeAdapter trading
|
|
6
6
|
* methods, so an adapter can implement both without overloads.
|
|
@@ -26,7 +26,7 @@ export interface TradingV2 extends VenueMarketsSource {
|
|
|
26
26
|
}
|
|
27
27
|
export declare function isTradingV2(a: unknown): a is TradingV2;
|
|
28
28
|
/**
|
|
29
|
-
* Uniform "not supported" response
|
|
29
|
+
* Uniform "not supported" response: CAPABILITY_UNSUPPORTED
|
|
30
30
|
* with details { venue, feature, reason } and an alternative in `remediation`.
|
|
31
31
|
* `venue` is a venue id or display name; `feature` a short capability name
|
|
32
32
|
* ("limitOrder", "fok", "postOnly", "amend", "trading", "orderbook", ...).
|
|
@@ -72,7 +72,7 @@ export declare function validateOrderRequest(venue: VenueDefinition, caps: Venue
|
|
|
72
72
|
/**
|
|
73
73
|
* Resolve a CLI TIF input (common GTC/IOC/FOK/ALO or a venue alias GTT/GTX/TOB,
|
|
74
74
|
* case-insensitive) to a value the venue declares in its capabilities. Never
|
|
75
|
-
* substitutes a different meaning: FOK is never emulated
|
|
75
|
+
* substitutes a different meaning: FOK is never emulated and an
|
|
76
76
|
* unknown / unoffered value is the uniform CAPABILITY_UNSUPPORTED response.
|
|
77
77
|
*/
|
|
78
78
|
export declare function validateLegacyTif(venue: VenueDefinition, caps: VenueCapabilities, tif: string): TimeInForce;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* TradingV2 — normalized order entry for venues added under the venue
|
|
3
|
-
* abstraction
|
|
3
|
+
* abstraction.
|
|
4
4
|
*
|
|
5
5
|
* Method names deliberately do not overlap the legacy ExchangeAdapter trading
|
|
6
6
|
* methods, so an adapter can implement both without overloads.
|
|
@@ -18,7 +18,7 @@ export function isTradingV2(a) {
|
|
|
18
18
|
&& typeof r.getVenueMarkets === "function";
|
|
19
19
|
}
|
|
20
20
|
/**
|
|
21
|
-
* Uniform "not supported" response
|
|
21
|
+
* Uniform "not supported" response: CAPABILITY_UNSUPPORTED
|
|
22
22
|
* with details { venue, feature, reason } and an alternative in `remediation`.
|
|
23
23
|
* `venue` is a venue id or display name; `feature` a short capability name
|
|
24
24
|
* ("limitOrder", "fok", "postOnly", "amend", "trading", "orderbook", ...).
|
|
@@ -113,7 +113,7 @@ export function validateOrderRequest(venue, caps, req) {
|
|
|
113
113
|
requirePositive(req.price, "price");
|
|
114
114
|
if (req.triggerPrice !== null)
|
|
115
115
|
requirePositive(req.triggerPrice, "triggerPrice");
|
|
116
|
-
// Callers resolve the common default (GTC
|
|
116
|
+
// Callers resolve the common default (GTC) before building the request;
|
|
117
117
|
// a missing TIF here is a programming error, never a user-facing TIF_REQUIRED.
|
|
118
118
|
if (needsPrice && req.tif === null) {
|
|
119
119
|
throw new PerpError("INVALID_PARAMS", `${venue.displayName} ${req.type} order is missing its resolved time-in-force`);
|
|
@@ -132,7 +132,7 @@ export function validateOrderRequest(venue, caps, req) {
|
|
|
132
132
|
const alternative = `set a take-profit / stop-loss on the position instead: perp trade tpsl ${req.symbol} <long|short> --tp <price>${caps.tpsl !== null && caps.tpsl !== "positionLevel" ? " [--size <qty>]" : ""} -e ${venue.id}`;
|
|
133
133
|
throw unsupported(venue.displayName, "restingReduceOnly", `a reduce-only limit order can only be ${caps.reduceOnlyTif.join(" / ")} (got ${req.tif})`, `Use --tif ${caps.reduceOnlyTif.join(" | ")}, or ${alternative}`);
|
|
134
134
|
}
|
|
135
|
-
// Callers always pass resolveSlippage() output for market orders
|
|
135
|
+
// Callers always pass resolveSlippage() output for market orders; a
|
|
136
136
|
// missing bound here is a programming error, never "send unbounded".
|
|
137
137
|
if (req.type === "market" && req.slippage === null) {
|
|
138
138
|
throw new PerpError("INVALID_PARAMS", `${venue.displayName} market order is missing its resolved slippage bound`);
|
|
@@ -159,7 +159,7 @@ export function validateOrderRequest(venue, caps, req) {
|
|
|
159
159
|
}
|
|
160
160
|
}
|
|
161
161
|
/**
|
|
162
|
-
* Common TIF aliases
|
|
162
|
+
* Common TIF aliases: a common name the venue lists under its own
|
|
163
163
|
* spelling, and venue spellings accepted for the common name. Only same-meaning
|
|
164
164
|
* pairs: GTC ↔ GTT (resting until cancelled / venue expiry), ALO ↔ GTX (post-only).
|
|
165
165
|
*/
|
|
@@ -172,7 +172,7 @@ const TIF_ALIASES = {
|
|
|
172
172
|
/**
|
|
173
173
|
* Resolve a CLI TIF input (common GTC/IOC/FOK/ALO or a venue alias GTT/GTX/TOB,
|
|
174
174
|
* case-insensitive) to a value the venue declares in its capabilities. Never
|
|
175
|
-
* substitutes a different meaning: FOK is never emulated
|
|
175
|
+
* substitutes a different meaning: FOK is never emulated and an
|
|
176
176
|
* unknown / unoffered value is the uniform CAPABILITY_UNSUPPORTED response.
|
|
177
177
|
*/
|
|
178
178
|
export function validateLegacyTif(venue, caps, tif) {
|
|
@@ -250,6 +250,9 @@ export function newVenueClientOrderId(venue) {
|
|
|
250
250
|
return randomUUID();
|
|
251
251
|
if (venue.family === "hyperliquid")
|
|
252
252
|
return `0x${randomBytes(16).toString("hex")}`;
|
|
253
|
+
// RISEx: uint64 decimal — ms timestamp × 1e6 + 6 random digits (< 2^64).
|
|
254
|
+
if (venue.family === "risex")
|
|
255
|
+
return (BigInt(Date.now()) * 1000000n + BigInt(randomBytes(4).readUInt32BE(0) % 1_000_000)).toString();
|
|
253
256
|
return newClientOrderId();
|
|
254
257
|
}
|
|
255
258
|
/** Finality wait after an async (orderAck "async") placement ack. */
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TxFlow public-only adapter — UNOFFICIAL read-only API (see venues/txflow.ts).
|
|
3
|
+
*
|
|
4
|
+
* Market data (markets, orderbook, trades, klines) is implemented. Funding history is not
|
|
5
|
+
* readable (the unofficial API refuses it) and every account / trading method refuses with
|
|
6
|
+
* CAPABILITY_UNSUPPORTED before any request — there is no TxFlow account or order code.
|
|
7
|
+
*/
|
|
8
|
+
import type { ExchangeAdapter, ExchangeBalance, ExchangeFundingPayment, ExchangeKline, ExchangeMarketInfo, ExchangeOrder, ExchangePosition, ExchangeTrade, ExchangePublicTrade } from "../interface.js";
|
|
9
|
+
import { type VenueDefinition } from "../venue.js";
|
|
10
|
+
import { type Network, type VenueMarket } from "../venue-types.js";
|
|
11
|
+
/** Candle intervals the TxFlow app requests (HL-style). */
|
|
12
|
+
export declare const TXFLOW_KLINE_INTERVALS: ReadonlySet<string>;
|
|
13
|
+
/** Legacy market row plus the labels every TxFlow row carries in JSON output. */
|
|
14
|
+
export type TxflowMarketInfo = ExchangeMarketInfo & {
|
|
15
|
+
venueApi: "unofficial";
|
|
16
|
+
fundingVerified: false;
|
|
17
|
+
};
|
|
18
|
+
export declare class TxflowPublicAdapter implements ExchangeAdapter {
|
|
19
|
+
readonly venue: VenueDefinition;
|
|
20
|
+
readonly network: Network;
|
|
21
|
+
readonly name: "txflow";
|
|
22
|
+
readonly aliases: readonly ["txf"];
|
|
23
|
+
private readonly client;
|
|
24
|
+
private dir;
|
|
25
|
+
constructor(venue: VenueDefinition, network: Network);
|
|
26
|
+
private market;
|
|
27
|
+
getVenueMarkets(): Promise<VenueMarket[]>;
|
|
28
|
+
getMarkets(): Promise<TxflowMarketInfo[]>;
|
|
29
|
+
getOrderbook(symbol: string): Promise<{
|
|
30
|
+
bids: [string, string][];
|
|
31
|
+
asks: [string, string][];
|
|
32
|
+
}>;
|
|
33
|
+
getRecentTrades(symbol: string, limit?: number): Promise<ExchangePublicTrade[]>;
|
|
34
|
+
getFundingHistory(): Promise<{
|
|
35
|
+
time: number;
|
|
36
|
+
rate: string;
|
|
37
|
+
price: string | null;
|
|
38
|
+
}[]>;
|
|
39
|
+
getKlines(symbol: string, interval: string, startTime: number, endTime: number): Promise<ExchangeKline[]>;
|
|
40
|
+
private refuse;
|
|
41
|
+
getBalance(): Promise<ExchangeBalance>;
|
|
42
|
+
getPositions(): Promise<ExchangePosition[]>;
|
|
43
|
+
getOpenOrders(): Promise<ExchangeOrder[]>;
|
|
44
|
+
getOrderHistory(): Promise<ExchangeOrder[]>;
|
|
45
|
+
getTradeHistory(): Promise<ExchangeTrade[]>;
|
|
46
|
+
getFundingPayments(): Promise<ExchangeFundingPayment[]>;
|
|
47
|
+
marketOrder(): Promise<unknown>;
|
|
48
|
+
limitOrder(): Promise<unknown>;
|
|
49
|
+
editOrder(): Promise<unknown>;
|
|
50
|
+
cancelOrder(): Promise<unknown>;
|
|
51
|
+
cancelAllOrders(): Promise<unknown>;
|
|
52
|
+
setLeverage(): Promise<unknown>;
|
|
53
|
+
stopOrder(): Promise<unknown>;
|
|
54
|
+
}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TxFlow public-only adapter — UNOFFICIAL read-only API (see venues/txflow.ts).
|
|
3
|
+
*
|
|
4
|
+
* Market data (markets, orderbook, trades, klines) is implemented. Funding history is not
|
|
5
|
+
* readable (the unofficial API refuses it) and every account / trading method refuses with
|
|
6
|
+
* CAPABILITY_UNSUPPORTED before any request — there is no TxFlow account or order code.
|
|
7
|
+
*/
|
|
8
|
+
import { PerpError } from "../../errors.js";
|
|
9
|
+
import { unsupported } from "../trading-v2.js";
|
|
10
|
+
import { roleUnsupported, networkConfig } from "../venue.js";
|
|
11
|
+
import { toLegacyMarketInfo } from "../venue-types.js";
|
|
12
|
+
import { txflowShapeError, TxflowClient } from "./client.js";
|
|
13
|
+
import { fetchTxflowDirectory, fetchTxflowMarkets, resolveTxflowMarket } from "./public.js";
|
|
14
|
+
/** Candle intervals the TxFlow app requests (HL-style). */
|
|
15
|
+
export const TXFLOW_KLINE_INTERVALS = new Set(["1m", "3m", "5m", "15m", "30m", "1h", "2h", "4h", "8h", "12h", "1d", "3d", "1w", "1M"]);
|
|
16
|
+
function dec(v, what) {
|
|
17
|
+
if (typeof v !== "string" || !/^-?\d+(\.\d+)?$/.test(v))
|
|
18
|
+
throw txflowShapeError(`${what} is not a decimal string`);
|
|
19
|
+
return v;
|
|
20
|
+
}
|
|
21
|
+
export class TxflowPublicAdapter {
|
|
22
|
+
venue;
|
|
23
|
+
network;
|
|
24
|
+
name = "txflow";
|
|
25
|
+
aliases = ["txf"];
|
|
26
|
+
client;
|
|
27
|
+
dir = null;
|
|
28
|
+
constructor(venue, network) {
|
|
29
|
+
this.venue = venue;
|
|
30
|
+
this.network = network;
|
|
31
|
+
this.client = new TxflowClient(networkConfig(venue, network).restUrl, venue.rateLimit);
|
|
32
|
+
}
|
|
33
|
+
async market(symbol) {
|
|
34
|
+
if (this.dir === null) {
|
|
35
|
+
this.dir = fetchTxflowDirectory(this.client);
|
|
36
|
+
this.dir.catch(() => { this.dir = null; });
|
|
37
|
+
}
|
|
38
|
+
return resolveTxflowMarket(await this.dir, symbol);
|
|
39
|
+
}
|
|
40
|
+
// ── Market data ──
|
|
41
|
+
async getVenueMarkets() {
|
|
42
|
+
return fetchTxflowMarkets(this.network);
|
|
43
|
+
}
|
|
44
|
+
async getMarkets() {
|
|
45
|
+
return (await this.getVenueMarkets()).map(m => ({ ...toLegacyMarketInfo(m), venueApi: "unofficial", fundingVerified: false }));
|
|
46
|
+
}
|
|
47
|
+
async getOrderbook(symbol) {
|
|
48
|
+
const m = await this.market(symbol);
|
|
49
|
+
const res = await this.client.info({ type: "l2Book", coin: String(m.index) });
|
|
50
|
+
const levels = res?.levels;
|
|
51
|
+
if (!Array.isArray(levels) || levels.length !== 2 || !Array.isArray(levels[0]) || !Array.isArray(levels[1])) {
|
|
52
|
+
throw txflowShapeError("l2Book has no [bids, asks] levels");
|
|
53
|
+
}
|
|
54
|
+
const side = (rows) => rows.map(r => {
|
|
55
|
+
const x = r;
|
|
56
|
+
return [dec(x.px, "l2Book px"), dec(x.sz, "l2Book sz")];
|
|
57
|
+
});
|
|
58
|
+
return { bids: side(levels[0]), asks: side(levels[1]) };
|
|
59
|
+
}
|
|
60
|
+
async getRecentTrades(symbol, limit = 50) {
|
|
61
|
+
const m = await this.market(symbol);
|
|
62
|
+
const res = await this.client.info({ type: "trades", coin: String(m.index) });
|
|
63
|
+
if (!Array.isArray(res))
|
|
64
|
+
throw txflowShapeError("trades is not an array");
|
|
65
|
+
return res.slice(0, limit).map(t => {
|
|
66
|
+
const x = t;
|
|
67
|
+
if (x.side !== "B" && x.side !== "A")
|
|
68
|
+
throw txflowShapeError(`trade side ${String(x.side)}`);
|
|
69
|
+
if (typeof x.time !== "number")
|
|
70
|
+
throw txflowShapeError("trade without time");
|
|
71
|
+
// Public tape carries no fee; "0" follows the legacy ExchangeTrade convention for public trades.
|
|
72
|
+
return { time: x.time, symbol: m.symbol, side: x.side === "B" ? "buy" : "sell", price: dec(x.px, "trade px"), size: dec(x.sz, "trade sz"), fee: null };
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
async getFundingHistory() {
|
|
76
|
+
throw unsupported("txflow", "fundingHistory", "TxFlow's unofficial API refuses funding-history reads (HTTP 403)", "Current TxFlow funding (unverified, per hour): perp -e txflow market info <symbol>, or perp arb scan --rates --include-unverified");
|
|
77
|
+
}
|
|
78
|
+
async getKlines(symbol, interval, startTime, endTime) {
|
|
79
|
+
if (!TXFLOW_KLINE_INTERVALS.has(interval)) {
|
|
80
|
+
throw new PerpError("INVALID_PARAMS", `TxFlow: unsupported candle interval "${interval}"`, {
|
|
81
|
+
remediation: `Use one of: ${[...TXFLOW_KLINE_INTERVALS].join(", ")}`,
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
const m = await this.market(symbol);
|
|
85
|
+
const res = await this.client.info({ type: "candleSnapshot", req: { coin: String(m.index), interval, startTime, endTime } });
|
|
86
|
+
if (!Array.isArray(res))
|
|
87
|
+
throw txflowShapeError("candleSnapshot is not an array");
|
|
88
|
+
return res.map(c => {
|
|
89
|
+
const x = c;
|
|
90
|
+
if (typeof x.t !== "number")
|
|
91
|
+
throw txflowShapeError("candle without t");
|
|
92
|
+
return {
|
|
93
|
+
time: x.t,
|
|
94
|
+
open: dec(x.o, "candle o"), high: dec(x.h, "candle h"), low: dec(x.l, "candle l"), close: dec(x.c, "candle c"),
|
|
95
|
+
volume: dec(x.v, "candle v"),
|
|
96
|
+
quoteVolume: null,
|
|
97
|
+
trades: typeof x.n === "number" ? x.n : null,
|
|
98
|
+
};
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
// ── Account / trading: refused (market-data-only venue, unofficial API) ──
|
|
102
|
+
refuse(feature) {
|
|
103
|
+
throw roleUnsupported(this.venue, feature);
|
|
104
|
+
}
|
|
105
|
+
async getBalance() { return this.refuse("account"); }
|
|
106
|
+
async getPositions() { return this.refuse("account"); }
|
|
107
|
+
async getOpenOrders() { return this.refuse("account"); }
|
|
108
|
+
async getOrderHistory() { return this.refuse("account"); }
|
|
109
|
+
async getTradeHistory() { return this.refuse("account"); }
|
|
110
|
+
async getFundingPayments() { return this.refuse("account"); }
|
|
111
|
+
async marketOrder() { return this.refuse("trading"); }
|
|
112
|
+
async limitOrder() { return this.refuse("trading"); }
|
|
113
|
+
async editOrder() { return this.refuse("trading"); }
|
|
114
|
+
async cancelOrder() { return this.refuse("trading"); }
|
|
115
|
+
async cancelAllOrders() { return this.refuse("trading"); }
|
|
116
|
+
async setLeverage() { return this.refuse("trading"); }
|
|
117
|
+
async stopOrder() { return this.refuse("trading"); }
|
|
118
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TxFlow transport — UNOFFICIAL API (see venues/txflow.ts).
|
|
3
|
+
*
|
|
4
|
+
* - REST: `POST <rest>/info` with an HL-style `{type, ...}` body. Errors come back three ways and
|
|
5
|
+
* are all checked: a non-2xx status (403 "This action is not allowed." = the read is not on the
|
|
6
|
+
* venue's allowlist), HTTP 200 with a `{message}` body, or a body of an unexpected shape.
|
|
7
|
+
* There is no fallback for a refused read (no HL-endpoint substitution, no guessing).
|
|
8
|
+
* - WS one-shot: open → subscribe `allAssetCtxs` → first data frame → close (bulk mark / funding
|
|
9
|
+
* snapshot). The socket factory is injectable for tests.
|
|
10
|
+
*/
|
|
11
|
+
import { PerpError } from "../../errors.js";
|
|
12
|
+
import type { RateLimitPolicy } from "../venue.js";
|
|
13
|
+
export declare const TXFLOW_HTTP_TIMEOUT_MS = 15000;
|
|
14
|
+
export declare const TXFLOW_WS_SNAPSHOT_TIMEOUT_MS = 10000;
|
|
15
|
+
/** Shape drift in the unofficial API: an explicit EXCHANGE_ERROR, never a guessed value. */
|
|
16
|
+
export declare function txflowShapeError(what: string): PerpError;
|
|
17
|
+
export declare class TxflowClient {
|
|
18
|
+
private readonly restUrl;
|
|
19
|
+
private readonly rateLimit;
|
|
20
|
+
constructor(restUrl: string, rateLimit: RateLimitPolicy);
|
|
21
|
+
/** POST /info. Returns the parsed body; every error path is a PerpError. */
|
|
22
|
+
info<T = unknown>(body: {
|
|
23
|
+
type: string;
|
|
24
|
+
} & Record<string, unknown>): Promise<T>;
|
|
25
|
+
}
|
|
26
|
+
/** The subset of a `ws` WebSocket the one-shot reader uses (a fake in tests). */
|
|
27
|
+
export interface WsLike {
|
|
28
|
+
on(event: "open", cb: () => void): unknown;
|
|
29
|
+
on(event: "message", cb: (data: unknown) => void): unknown;
|
|
30
|
+
on(event: "error", cb: (err: unknown) => void): unknown;
|
|
31
|
+
on(event: "close", cb: (code: number) => void): unknown;
|
|
32
|
+
send(data: string): void;
|
|
33
|
+
close(): void;
|
|
34
|
+
}
|
|
35
|
+
export type WsFactory = (url: string) => WsLike;
|
|
36
|
+
export declare const defaultWsFactory: WsFactory;
|
|
37
|
+
/** One raw `allAssetCtxs` entry: `coin` is the perpMeta `index` as a decimal string. */
|
|
38
|
+
export interface TxflowRawAssetCtx {
|
|
39
|
+
coin: string;
|
|
40
|
+
ctx: Record<string, unknown>;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Open the socket, subscribe to `allAssetCtxs`, resolve with the first data frame's `ctxs`, close.
|
|
44
|
+
* No frame within the timeout (or a socket error / close first) → EXCHANGE_UNREACHABLE; a frame
|
|
45
|
+
* of an unexpected shape → EXCHANGE_ERROR (shape drift).
|
|
46
|
+
*/
|
|
47
|
+
export declare function fetchAllAssetCtxsOnce(wsUrl: string, factory?: WsFactory, timeoutMs?: number): Promise<TxflowRawAssetCtx[]>;
|