perp-cli 0.12.15 → 0.14.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 +278 -31
- package/dist/_stderr-filter.js +0 -1
- package/dist/agent-wallet/lighter-keystore.d.ts +11 -0
- package/dist/agent-wallet/lighter-keystore.js +92 -10
- package/dist/agent-wallet/passphrase.d.ts +1 -1
- package/dist/agent-wallet/passphrase.js +16 -3
- package/dist/agent-wallet/store.d.ts +37 -4
- package/dist/agent-wallet/store.js +132 -0
- package/dist/agent-wallet/venue-keystore.d.ts +14 -0
- package/dist/agent-wallet/venue-keystore.js +121 -0
- package/dist/api/public/hyperliquid.js +4 -4
- package/dist/api/public/lighter.js +5 -5
- package/dist/api/public/pacifica.d.ts +0 -1
- package/dist/api/public/pacifica.js +4 -13
- package/dist/api/public/urls.d.ts +9 -1
- package/dist/api/public/urls.js +35 -13
- package/dist/arb/file-lock.d.ts +11 -0
- package/dist/arb/file-lock.js +74 -0
- package/dist/arb/index.d.ts +2 -2
- package/dist/arb/index.js +2 -2
- package/dist/arb/pending-entry.d.ts +32 -0
- package/dist/arb/pending-entry.js +77 -0
- package/dist/arb/sizing.d.ts +6 -13
- package/dist/arb/sizing.js +12 -51
- package/dist/arb/state.d.ts +45 -12
- package/dist/arb/state.js +214 -42
- package/dist/arb/utils.d.ts +20 -6
- package/dist/arb/utils.js +43 -39
- package/dist/bot/conditions.d.ts +4 -4
- package/dist/bot/conditions.js +11 -6
- package/dist/bot/config.d.ts +17 -1
- package/dist/bot/config.js +48 -12
- package/dist/bot/engine.d.ts +61 -2
- package/dist/bot/engine.js +610 -144
- package/dist/bot/fill-size.d.ts +7 -0
- package/dist/bot/fill-size.js +18 -0
- package/dist/bot/index.d.ts +1 -1
- package/dist/bot/index.js +1 -1
- package/dist/bot/perp-order.d.ts +99 -0
- package/dist/bot/perp-order.js +299 -0
- package/dist/bot/presets.d.ts +7 -0
- package/dist/bot/presets.js +17 -0
- package/dist/bot/quarantine.d.ts +75 -0
- package/dist/bot/quarantine.js +190 -0
- package/dist/bot/quote-rules.d.ts +23 -0
- package/dist/bot/quote-rules.js +69 -0
- package/dist/bot/strategies/avellaneda-mm.d.ts +32 -2
- package/dist/bot/strategies/avellaneda-mm.js +72 -70
- package/dist/bot/strategies/basis-arb.js +2 -0
- package/dist/bot/strategies/dca-strategy.d.ts +2 -0
- package/dist/bot/strategies/dca-strategy.js +5 -0
- package/dist/bot/strategies/funding-arb-strategy.d.ts +43 -3
- package/dist/bot/strategies/funding-arb-strategy.js +571 -380
- package/dist/bot/strategies/funding-arb-utils.d.ts +48 -23
- package/dist/bot/strategies/funding-arb-utils.js +159 -195
- package/dist/bot/strategies/funding-arb-v2.d.ts +32 -0
- package/dist/bot/strategies/funding-arb-v2.js +258 -47
- package/dist/bot/strategies/funding-auto.js +215 -194
- package/dist/bot/strategies/spot-perp-arb-strategy.d.ts +14 -0
- package/dist/bot/strategies/spot-perp-arb-strategy.js +217 -50
- package/dist/bot/strategies/spot-perp-executor.d.ts +73 -19
- package/dist/bot/strategies/spot-perp-executor.js +319 -120
- package/dist/bot/strategies/spot-perp-state.d.ts +43 -0
- package/dist/bot/strategies/spot-perp-state.js +70 -0
- package/dist/bot/strategy-types.d.ts +6 -0
- package/dist/bot/tui/BotDashboard.d.ts +7 -2
- package/dist/bot/tui/BotDashboard.js +6 -3
- package/dist/bridge-engine.d.ts +120 -19
- package/dist/bridge-engine.js +710 -422
- package/dist/bridge-signers.d.ts +54 -0
- package/dist/bridge-signers.js +161 -0
- package/dist/cache.d.ts +4 -1
- package/dist/cache.js +11 -0
- package/dist/cli-flags.d.ts +50 -0
- package/dist/cli-flags.js +104 -0
- package/dist/cli-program.d.ts +35 -0
- package/dist/cli-program.js +286 -0
- package/dist/cli-spec.js +1 -1
- package/dist/client-id-tracker.js +11 -2
- package/dist/commands/account.d.ts +7 -1
- package/dist/commands/account.js +74 -84
- package/dist/commands/agent-import.d.ts +114 -0
- package/dist/commands/agent-import.js +794 -0
- package/dist/commands/agent.d.ts +232 -2
- package/dist/commands/agent.js +1858 -955
- package/dist/commands/alerts.d.ts +1 -0
- package/dist/commands/alerts.js +31 -28
- package/dist/commands/arb/index.d.ts +60 -1
- package/dist/commands/arb/index.js +381 -384
- package/dist/commands/arb-auto.d.ts +132 -4
- package/dist/commands/arb-auto.js +1474 -967
- package/dist/commands/arb.d.ts +3 -1
- package/dist/commands/arb.js +62 -30
- package/dist/commands/bot.js +127 -47
- package/dist/commands/bridge.d.ts +2 -1
- package/dist/commands/bridge.js +339 -192
- package/dist/commands/builder.d.ts +55 -0
- package/dist/commands/builder.js +387 -0
- package/dist/commands/funds-onchain.d.ts +32 -0
- package/dist/commands/funds-onchain.js +286 -0
- package/dist/commands/funds.d.ts +25 -0
- package/dist/commands/funds.js +939 -245
- package/dist/commands/history.js +72 -45
- package/dist/commands/init.d.ts +107 -15
- package/dist/commands/init.js +1116 -249
- package/dist/commands/jobs.d.ts +2 -1
- package/dist/commands/jobs.js +77 -15
- package/dist/commands/manage.js +136 -91
- package/dist/commands/market.d.ts +4 -1
- package/dist/commands/market.js +35 -32
- package/dist/commands/outcome.d.ts +10 -0
- package/dist/commands/outcome.js +396 -0
- package/dist/commands/plan.js +52 -12
- package/dist/commands/portfolio.d.ts +1 -0
- package/dist/commands/portfolio.js +60 -72
- package/dist/commands/rebalance.d.ts +2 -1
- package/dist/commands/rebalance.js +33 -43
- package/dist/commands/risk.js +46 -19
- package/dist/commands/settings.d.ts +1 -0
- package/dist/commands/settings.js +246 -51
- package/dist/commands/trade.d.ts +50 -0
- package/dist/commands/trade.js +1943 -695
- package/dist/commands/wallet.d.ts +30 -1
- package/dist/commands/wallet.js +316 -337
- package/dist/config.d.ts +60 -9
- package/dist/config.js +252 -82
- package/dist/constants.d.ts +29 -4
- package/dist/constants.js +62 -20
- package/dist/dashboard/server.d.ts +2 -0
- package/dist/dashboard/server.js +9 -6
- package/dist/dashboard/ui.js +6 -4
- package/dist/dashboard/ws-feeds.js +20 -8
- package/dist/dex-asset-map.js +5 -3
- package/dist/errors.d.ts +122 -0
- package/dist/errors.js +194 -0
- package/dist/event-stream.js +75 -19
- package/dist/evm-master-tx.d.ts +59 -0
- package/dist/evm-master-tx.js +154 -0
- package/dist/exchanges/adapter-network.d.ts +10 -0
- package/dist/exchanges/adapter-network.js +10 -0
- package/dist/exchanges/arcus/adapter.d.ts +196 -0
- package/dist/exchanges/arcus/adapter.js +1092 -0
- package/dist/exchanges/arcus/agent.d.ts +55 -0
- package/dist/exchanges/arcus/agent.js +188 -0
- package/dist/exchanges/arcus/client.d.ts +63 -0
- package/dist/exchanges/arcus/client.js +285 -0
- package/dist/exchanges/arcus/public.d.ts +65 -0
- package/dist/exchanges/arcus/public.js +138 -0
- package/dist/exchanges/arcus/signing.d.ts +176 -0
- package/dist/exchanges/arcus/signing.js +288 -0
- package/dist/exchanges/arcus/spot-chain.d.ts +60 -0
- package/dist/exchanges/arcus/spot-chain.js +241 -0
- package/dist/exchanges/arcus/spot-router.d.ts +177 -0
- package/dist/exchanges/arcus/spot-router.js +183 -0
- package/dist/exchanges/arcus/spot.d.ts +109 -0
- package/dist/exchanges/arcus/spot.js +645 -0
- package/dist/exchanges/aster-funds.d.ts +127 -0
- package/dist/exchanges/aster-funds.js +252 -0
- package/dist/exchanges/aster-nonce.d.ts +2 -0
- package/dist/exchanges/aster-nonce.js +107 -0
- package/dist/exchanges/aster-spot.d.ts +117 -0
- package/dist/exchanges/aster-spot.js +686 -0
- package/dist/exchanges/aster-typed-data.d.ts +133 -7
- package/dist/exchanges/aster-typed-data.js +121 -7
- package/dist/exchanges/aster.d.ts +219 -6
- package/dist/exchanges/aster.js +1023 -153
- package/dist/exchanges/builder-fee.d.ts +254 -0
- package/dist/exchanges/builder-fee.js +421 -0
- package/dist/exchanges/canonical-symbols.d.ts +32 -0
- package/dist/exchanges/canonical-symbols.js +96 -0
- package/dist/exchanges/capabilities.d.ts +7 -1
- package/dist/exchanges/capabilities.js +7 -0
- package/dist/exchanges/hl-signing.d.ts +165 -0
- package/dist/exchanges/hl-signing.js +158 -0
- package/dist/exchanges/hyperliquid-outcome.d.ts +211 -0
- package/dist/exchanges/hyperliquid-outcome.js +646 -0
- package/dist/exchanges/hyperliquid-spot.d.ts +44 -5
- package/dist/exchanges/hyperliquid-spot.js +308 -117
- package/dist/exchanges/hyperliquid.d.ts +462 -57
- package/dist/exchanges/hyperliquid.js +1575 -449
- package/dist/exchanges/index.d.ts +6 -0
- package/dist/exchanges/index.js +4 -0
- package/dist/exchanges/interface.d.ts +63 -10
- package/dist/exchanges/interface.js +18 -3
- package/dist/exchanges/ioc-outcome.d.ts +44 -0
- package/dist/exchanges/ioc-outcome.js +57 -0
- package/dist/exchanges/legacy-trading-v2.d.ts +39 -0
- package/dist/exchanges/legacy-trading-v2.js +940 -0
- package/dist/exchanges/lighter-public.d.ts +43 -0
- package/dist/exchanges/lighter-public.js +276 -0
- package/dist/exchanges/lighter-rh-deposit.d.ts +62 -0
- package/dist/exchanges/lighter-rh-deposit.js +185 -0
- package/dist/exchanges/lighter-signer.d.ts +123 -0
- package/dist/exchanges/lighter-signer.js +215 -0
- package/dist/exchanges/lighter-spot.d.ts +91 -13
- package/dist/exchanges/lighter-spot.js +595 -233
- package/dist/exchanges/lighter.d.ts +611 -39
- package/dist/exchanges/lighter.js +1890 -649
- package/dist/exchanges/ondo/adapter.d.ts +166 -0
- package/dist/exchanges/ondo/adapter.js +835 -0
- package/dist/exchanges/ondo/agent.d.ts +31 -0
- package/dist/exchanges/ondo/agent.js +101 -0
- package/dist/exchanges/ondo/client.d.ts +60 -0
- package/dist/exchanges/ondo/client.js +297 -0
- package/dist/exchanges/ondo/funds.d.ts +122 -0
- package/dist/exchanges/ondo/funds.js +290 -0
- package/dist/exchanges/ondo/public.d.ts +95 -0
- package/dist/exchanges/ondo/public.js +214 -0
- package/dist/exchanges/ondo/spot.d.ts +127 -0
- package/dist/exchanges/ondo/spot.js +392 -0
- package/dist/exchanges/outcome-interface.d.ts +140 -0
- package/dist/exchanges/outcome-interface.js +16 -0
- package/dist/exchanges/pacifica-spot.d.ts +83 -0
- package/dist/exchanges/pacifica-spot.js +324 -0
- package/dist/exchanges/pacifica-typed-data.d.ts +30 -13
- package/dist/exchanges/pacifica-typed-data.js +57 -9
- package/dist/exchanges/pacifica.d.ts +288 -4
- package/dist/exchanges/pacifica.js +1013 -76
- package/dist/exchanges/precision.d.ts +47 -0
- package/dist/exchanges/precision.js +209 -0
- package/dist/exchanges/qfex/adapter.d.ts +341 -0
- package/dist/exchanges/qfex/adapter.js +1383 -0
- package/dist/exchanges/qfex/agent.d.ts +58 -0
- package/dist/exchanges/qfex/agent.js +188 -0
- package/dist/exchanges/qfex/client.d.ts +54 -0
- package/dist/exchanges/qfex/client.js +138 -0
- package/dist/exchanges/qfex/errors.d.ts +70 -0
- package/dist/exchanges/qfex/errors.js +236 -0
- package/dist/exchanges/qfex/public.d.ts +122 -0
- package/dist/exchanges/qfex/public.js +268 -0
- package/dist/exchanges/qfex/signing.d.ts +160 -0
- package/dist/exchanges/qfex/signing.js +283 -0
- package/dist/exchanges/qfex/ws-transport.d.ts +294 -0
- package/dist/exchanges/qfex/ws-transport.js +798 -0
- package/dist/exchanges/rate-limiter.d.ts +12 -0
- package/dist/exchanges/rate-limiter.js +64 -0
- package/dist/exchanges/registry.d.ts +14 -2
- package/dist/exchanges/registry.js +20 -32
- package/dist/exchanges/slippage.d.ts +37 -0
- package/dist/exchanges/slippage.js +70 -0
- package/dist/exchanges/spot-factory.d.ts +166 -0
- package/dist/exchanges/spot-factory.js +431 -0
- package/dist/exchanges/spot-interface.d.ts +98 -4
- package/dist/exchanges/spot-interface.js +27 -1
- package/dist/exchanges/spot-result.d.ts +79 -0
- package/dist/exchanges/spot-result.js +273 -0
- package/dist/exchanges/trading-v2.d.ts +117 -0
- package/dist/exchanges/trading-v2.js +301 -0
- package/dist/exchanges/venue-types.d.ts +231 -0
- package/dist/exchanges/venue-types.js +87 -0
- package/dist/exchanges/venue.d.ts +211 -0
- package/dist/exchanges/venue.js +132 -0
- package/dist/exchanges/venues/arcus.d.ts +8 -0
- package/dist/exchanges/venues/arcus.js +109 -0
- package/dist/exchanges/venues/aster.d.ts +49 -0
- package/dist/exchanges/venues/aster.js +176 -0
- package/dist/exchanges/venues/create-helpers.d.ts +18 -0
- package/dist/exchanges/venues/create-helpers.js +199 -0
- package/dist/exchanges/venues/hyperliquid.d.ts +23 -0
- package/dist/exchanges/venues/hyperliquid.js +126 -0
- package/dist/exchanges/venues/index.d.ts +16 -0
- package/dist/exchanges/venues/index.js +26 -0
- package/dist/exchanges/venues/lighter-family.d.ts +92 -0
- package/dist/exchanges/venues/lighter-family.js +166 -0
- package/dist/exchanges/venues/lighter-rh.d.ts +7 -0
- package/dist/exchanges/venues/lighter-rh.js +58 -0
- package/dist/exchanges/venues/lighter.d.ts +6 -0
- package/dist/exchanges/venues/lighter.js +55 -0
- package/dist/exchanges/venues/ondo.d.ts +34 -0
- package/dist/exchanges/venues/ondo.js +140 -0
- package/dist/exchanges/venues/pacifica.d.ts +8 -0
- package/dist/exchanges/venues/pacifica.js +95 -0
- package/dist/exchanges/venues/qfex.d.ts +28 -0
- package/dist/exchanges/venues/qfex.js +132 -0
- package/dist/execution-log.js +10 -2
- package/dist/funding/legacy-public.d.ts +31 -0
- package/dist/funding/legacy-public.js +369 -0
- package/dist/funding/normalize.d.ts +28 -16
- package/dist/funding/normalize.js +61 -29
- package/dist/funding/rates.d.ts +46 -11
- package/dist/funding/rates.js +228 -277
- package/dist/guardrail/perp-guardrail.js +0 -0
- package/dist/index.js +157 -682
- package/dist/jobs.d.ts +84 -6
- package/dist/jobs.js +239 -41
- package/dist/landing.d.ts +14 -0
- package/dist/landing.js +36 -0
- package/dist/mcp-server.d.ts +14 -4
- package/dist/mcp-server.js +820 -402
- package/dist/mcp-trade-guard.d.ts +16 -0
- package/dist/mcp-trade-guard.js +59 -0
- package/dist/pacifica/client.d.ts +41 -10
- package/dist/pacifica/client.js +167 -63
- package/dist/pacifica/constants.d.ts +0 -1
- package/dist/pacifica/constants.js +0 -5
- package/dist/pacifica/deposit.d.ts +30 -3
- package/dist/pacifica/deposit.js +119 -2
- package/dist/pacifica/faucet.d.ts +29 -0
- package/dist/pacifica/faucet.js +100 -0
- package/dist/pacifica/sign-tx.d.ts +15 -0
- package/dist/pacifica/sign-tx.js +41 -0
- package/dist/pacifica/types/account.d.ts +28 -4
- package/dist/pacifica/types/lake.d.ts +20 -5
- package/dist/pacifica/types/lake.js +4 -0
- package/dist/pacifica/types/market.d.ts +5 -0
- package/dist/pacifica/types/order.d.ts +4 -1
- package/dist/pacifica/types/position.d.ts +9 -1
- package/dist/plan-executor.d.ts +10 -5
- package/dist/plan-executor.js +165 -54
- package/dist/position-history.js +13 -0
- package/dist/rebalance.d.ts +26 -2
- package/dist/rebalance.js +86 -7
- package/dist/relay-swap.d.ts +232 -0
- package/dist/relay-swap.js +630 -0
- package/dist/risk.js +5 -1
- package/dist/secret-env.d.ts +15 -0
- package/dist/secret-env.js +36 -0
- package/dist/settings.d.ts +169 -6
- package/dist/settings.js +219 -38
- package/dist/signer/ows-evm.d.ts +13 -0
- package/dist/signer/ows-evm.js +22 -0
- package/dist/smart-order.d.ts +9 -0
- package/dist/smart-order.js +53 -8
- package/dist/strategies/dca.d.ts +6 -2
- package/dist/strategies/dca.js +66 -26
- package/dist/strategies/funding-arb.d.ts +11 -1
- package/dist/strategies/funding-arb.js +202 -89
- package/dist/strategies/grid.d.ts +31 -0
- package/dist/strategies/grid.js +89 -11
- package/dist/strategies/pending-stop.d.ts +8 -0
- package/dist/strategies/pending-stop.js +12 -0
- package/dist/strategies/split-order.d.ts +45 -17
- package/dist/strategies/split-order.js +157 -91
- package/dist/strategies/trailing-stop.d.ts +2 -1
- package/dist/strategies/trailing-stop.js +52 -6
- package/dist/strategies/twap.d.ts +6 -1
- package/dist/strategies/twap.js +39 -11
- package/dist/trade-validator.d.ts +22 -0
- package/dist/trade-validator.js +119 -15
- package/dist/utils/numeric.d.ts +46 -0
- package/dist/utils/numeric.js +79 -0
- package/dist/utils.d.ts +19 -0
- package/dist/utils.js +96 -19
- package/dist/withdraw-gate.d.ts +49 -0
- package/dist/withdraw-gate.js +131 -0
- package/package.json +15 -6
- package/skills/perp-cli/SKILL.md +338 -134
- package/skills/perp-cli/references/agent-operations.md +181 -92
- package/skills/perp-cli/references/commands.md +475 -132
- package/skills/perp-cli/references/strategies.md +44 -38
- package/skills/perp-cli/scripts/arb-monitor.sh +22 -17
- package/skills/perp-cli/scripts/funding-analysis.sh +20 -10
- package/skills/perp-cli/scripts/preflight.sh +21 -14
- package/skills/perp-cli/scripts/spot-perp-scan.sh +12 -7
- package/skills/perp-cli/scripts/validate-arb.sh +33 -16
- package/vendor/lighter-signer/SHA256SUMS +2 -0
- package/vendor/lighter-signer/VERSION +8 -0
- package/vendor/lighter-signer/lighter-signer.wasm +0 -0
- package/vendor/lighter-signer/wasm_exec.js +575 -0
- package/dist/bot/strategies/funding-arb-v2-strategy.d.ts +0 -42
- package/dist/bot/strategies/funding-arb-v2-strategy.js +0 -864
package/dist/smart-order.js
CHANGED
|
@@ -16,11 +16,34 @@
|
|
|
16
16
|
* - Saves 5-50+ bps vs raw market orders on thin books
|
|
17
17
|
* - Predictable execution price
|
|
18
18
|
*
|
|
19
|
+
* Order-outcome typing (design §12.3): every failure BEFORE the IOC is sent (orderbook read,
|
|
20
|
+
* empty book, unusable price), an explicit venue rejection embedded in the response, and an
|
|
21
|
+
* explicit IOC no-match (nothing filled, nothing rests) throw a definite ORDER_REJECTED
|
|
22
|
+
* (orderNotSent), so placePerpVerified does not quarantine a leg that never traded. Anything
|
|
23
|
+
* else the IOC call throws is rethrown unchanged (placePerpVerified measures it).
|
|
24
|
+
*
|
|
19
25
|
* SSOT rule #2: silent fallback is forbidden. The opt-in `fallback: true` flag
|
|
20
26
|
* is preserved for callers that explicitly want best-effort market substitution
|
|
21
27
|
* after IOC reject, but the DEFAULT is `false` — failure must surface as a
|
|
22
28
|
* thrown error, never a silent market sweep.
|
|
23
29
|
*/
|
|
30
|
+
import { alignComputedLimitPrice } from "./bot/quote-rules.js";
|
|
31
|
+
import { PerpError, orderNotSent } from "./errors.js";
|
|
32
|
+
import { resolveAdapterSlippage } from "./exchanges/slippage.js";
|
|
33
|
+
/**
|
|
34
|
+
* IOC "no match" signalled explicitly in the venue response (nothing filled, nothing rests):
|
|
35
|
+
* a Binance-style terminal status (Aster: EXPIRED / CANCELED / REJECTED) with executedQty 0.
|
|
36
|
+
* HL embedded errors (incl. "could not immediately match") are caught by hasOrderError.
|
|
37
|
+
*/
|
|
38
|
+
function isIocNoMatch(result) {
|
|
39
|
+
if (!result || typeof result !== "object")
|
|
40
|
+
return false;
|
|
41
|
+
const r = result;
|
|
42
|
+
const status = typeof r.status === "string" ? r.status.toUpperCase() : "";
|
|
43
|
+
if (!["EXPIRED", "CANCELED", "CANCELLED", "REJECTED"].includes(status))
|
|
44
|
+
return false;
|
|
45
|
+
return r.executedQty !== undefined && Number(r.executedQty) === 0;
|
|
46
|
+
}
|
|
24
47
|
/**
|
|
25
48
|
* Infer tick size from orderbook prices.
|
|
26
49
|
* Uses the minimum price increment visible in the book.
|
|
@@ -112,14 +135,25 @@ function describeOrderError(result) {
|
|
|
112
135
|
*/
|
|
113
136
|
export async function smartOrder(adapter, symbol, side, size, opts = {}) {
|
|
114
137
|
const { tickTolerance = 1, fallback = false, reduceOnly = false } = opts;
|
|
115
|
-
|
|
116
|
-
const
|
|
138
|
+
const venue = adapter.name;
|
|
139
|
+
const what = `smart ${side} ${size} ${symbol}${reduceOnly ? " (reduce-only)" : ""}`;
|
|
140
|
+
const marketFallback = () => adapter.marketOrder(symbol, side, size, {
|
|
141
|
+
reduceOnly, slippage: opts.slippage ?? resolveAdapterSlippage(adapter, null),
|
|
142
|
+
});
|
|
143
|
+
// 1. Fetch orderbook (failure = nothing sent)
|
|
144
|
+
let book;
|
|
145
|
+
try {
|
|
146
|
+
book = await adapter.getOrderbook(symbol);
|
|
147
|
+
}
|
|
148
|
+
catch (e) {
|
|
149
|
+
throw orderNotSent(venue, `${what} (orderbook read failed)`, e);
|
|
150
|
+
}
|
|
117
151
|
// 2. Validate book has liquidity on the relevant side
|
|
118
152
|
const relevantSide = side === "buy" ? book.asks : book.bids;
|
|
119
153
|
if (relevantSide.length === 0) {
|
|
120
154
|
if (!fallback)
|
|
121
|
-
throw new
|
|
122
|
-
const result = await
|
|
155
|
+
throw new PerpError("ORDER_REJECTED", `${venue} ${what} not sent: No ${side === "buy" ? "asks" : "bids"} in orderbook for ${symbol}`, { exchange: venue });
|
|
156
|
+
const result = await marketFallback();
|
|
123
157
|
return { result, method: "market_fallback", price: "0", bestBookPrice: "0", tickSize: "0" };
|
|
124
158
|
}
|
|
125
159
|
// 3. Get best price and tick size
|
|
@@ -131,7 +165,11 @@ export async function smartOrder(adapter, symbol, side, size, opts = {}) {
|
|
|
131
165
|
const limitPrice = side === "buy"
|
|
132
166
|
? bestPrice + tick * tickTolerance // 1 tick above best ask
|
|
133
167
|
: bestPrice - tick * tickTolerance; // 1 tick below best bid
|
|
134
|
-
|
|
168
|
+
// Book-inferred tick; venues without a published tick (HL) are then put on their own grid (passive side).
|
|
169
|
+
const formattedPrice = await alignComputedLimitPrice(adapter, symbol, side, limitPrice.toFixed(decimals));
|
|
170
|
+
if (!(Number.isFinite(bestPrice) && bestPrice > 0 && Number(formattedPrice) > 0)) {
|
|
171
|
+
throw new PerpError("ORDER_REJECTED", `${venue} ${what} not sent: unusable limit price ${formattedPrice} (best ${bestEntry[0]}, tick ${tick})`, { exchange: venue });
|
|
172
|
+
}
|
|
135
173
|
// 5. Place IOC limit order at calculated price
|
|
136
174
|
try {
|
|
137
175
|
const result = await adapter.limitOrder(symbol, side, formattedPrice, size, {
|
|
@@ -145,7 +183,7 @@ export async function smartOrder(adapter, symbol, side, size, opts = {}) {
|
|
|
145
183
|
// result for a rejected order and treat the failure as success.
|
|
146
184
|
if (hasOrderError(result)) {
|
|
147
185
|
if (fallback) {
|
|
148
|
-
const fallbackResult = await
|
|
186
|
+
const fallbackResult = await marketFallback();
|
|
149
187
|
return {
|
|
150
188
|
result: fallbackResult,
|
|
151
189
|
method: "market_fallback",
|
|
@@ -154,7 +192,14 @@ export async function smartOrder(adapter, symbol, side, size, opts = {}) {
|
|
|
154
192
|
tickSize: tick.toFixed(decimals),
|
|
155
193
|
};
|
|
156
194
|
}
|
|
157
|
-
throw new
|
|
195
|
+
throw new PerpError("ORDER_REJECTED", `${venue} ${what}: exchange rejected IOC limit (${describeOrderError(result)})`, { exchange: venue });
|
|
196
|
+
}
|
|
197
|
+
if (isIocNoMatch(result)) {
|
|
198
|
+
if (fallback) {
|
|
199
|
+
const fallbackResult = await marketFallback();
|
|
200
|
+
return { result: fallbackResult, method: "market_fallback", price: formattedPrice, bestBookPrice: bestEntry[0], tickSize: tick.toFixed(decimals) };
|
|
201
|
+
}
|
|
202
|
+
throw new PerpError("ORDER_REJECTED", `${venue} ${what}: IOC limit ${formattedPrice} matched nothing (nothing rests)`, { exchange: venue });
|
|
158
203
|
}
|
|
159
204
|
return {
|
|
160
205
|
result,
|
|
@@ -168,7 +213,7 @@ export async function smartOrder(adapter, symbol, side, size, opts = {}) {
|
|
|
168
213
|
if (!fallback)
|
|
169
214
|
throw err;
|
|
170
215
|
// Fallback to raw market order for reliability
|
|
171
|
-
const result = await
|
|
216
|
+
const result = await marketFallback();
|
|
172
217
|
return {
|
|
173
218
|
result,
|
|
174
219
|
method: "market_fallback",
|
package/dist/strategies/dca.d.ts
CHANGED
|
@@ -12,7 +12,11 @@ export interface DCAState {
|
|
|
12
12
|
ordersPlaced: number;
|
|
13
13
|
totalFilled: number;
|
|
14
14
|
totalCost: number;
|
|
15
|
-
|
|
15
|
+
/**
|
|
16
|
+
* Average fill price from the VENUE-reported average fill of every order (reportedAvgFillPrice),
|
|
17
|
+
* never the protection / limit price sent; null when any filled order reported none.
|
|
18
|
+
*/
|
|
19
|
+
avgPrice: number | null;
|
|
16
20
|
errors: number;
|
|
17
21
|
startedAt: number;
|
|
18
22
|
running: boolean;
|
|
@@ -20,6 +24,6 @@ export interface DCAState {
|
|
|
20
24
|
export declare function runDCA(adapter: ExchangeAdapter, params: DCAParams, jobId?: string, log?: (msg: string) => void): Promise<{
|
|
21
25
|
ordersPlaced: number;
|
|
22
26
|
totalFilled: number;
|
|
23
|
-
avgPrice: number;
|
|
27
|
+
avgPrice: number | null;
|
|
24
28
|
runtime: number;
|
|
25
29
|
}>;
|
package/dist/strategies/dca.js
CHANGED
|
@@ -1,12 +1,22 @@
|
|
|
1
|
+
import { symbolMatch } from "../utils.js";
|
|
2
|
+
import { assertCanClosePositions } from "../exchanges/trading-v2.js";
|
|
3
|
+
import { resolveAdapterSlippage } from "../exchanges/slippage.js";
|
|
1
4
|
import { updateJobState } from "../jobs.js";
|
|
5
|
+
import { reportedAvgFillPrice } from "../exchanges/ioc-outcome.js";
|
|
6
|
+
import { filledOf } from "../bot/fill-size.js";
|
|
7
|
+
import { isOutcomeUnknown } from "../bot/perp-order.js";
|
|
8
|
+
import { pendingStop } from "./pending-stop.js";
|
|
9
|
+
import { formatComputedPrice } from "../exchanges/precision.js";
|
|
10
|
+
import { venueRateString } from "../utils/numeric.js";
|
|
2
11
|
export async function runDCA(adapter, params, jobId, log = console.log) {
|
|
12
|
+
assertCanClosePositions(adapter, `DCA on ${adapter.name}`);
|
|
3
13
|
const { symbol, side, amountPerOrder, intervalSec, totalOrders } = params;
|
|
4
14
|
const maxRuntime = (params.maxRuntime ?? 0) * 1000;
|
|
5
15
|
const state = {
|
|
6
16
|
ordersPlaced: 0,
|
|
7
17
|
totalFilled: 0,
|
|
8
18
|
totalCost: 0,
|
|
9
|
-
avgPrice:
|
|
19
|
+
avgPrice: null,
|
|
10
20
|
errors: 0,
|
|
11
21
|
startedAt: Date.now(),
|
|
12
22
|
running: true,
|
|
@@ -20,6 +30,9 @@ export async function runDCA(adapter, params, jobId, log = console.log) {
|
|
|
20
30
|
const shutdown = () => { state.running = false; };
|
|
21
31
|
process.on("SIGINT", shutdown);
|
|
22
32
|
process.on("SIGTERM", shutdown);
|
|
33
|
+
let everyOrderPriced = true;
|
|
34
|
+
/** Set when an order's outcome / fill is unknown: the run stops and surfaces ORDER_PENDING. */
|
|
35
|
+
let pending = null;
|
|
23
36
|
try {
|
|
24
37
|
while (state.running) {
|
|
25
38
|
// Check if we've reached order limit
|
|
@@ -32,41 +45,58 @@ export async function runDCA(adapter, params, jobId, log = console.log) {
|
|
|
32
45
|
log(`[DCA] Max runtime reached. Stopping.`);
|
|
33
46
|
break;
|
|
34
47
|
}
|
|
35
|
-
// Check price limit
|
|
48
|
+
// Check price limit — the order goes out only when the price is KNOWN to be within it:
|
|
49
|
+
// a failed read / missing market / non-numeric price skips this order (never sent blind).
|
|
36
50
|
if (params.priceLimit) {
|
|
51
|
+
let price = null;
|
|
52
|
+
let why = "";
|
|
37
53
|
try {
|
|
38
54
|
const markets = await adapter.getMarkets();
|
|
39
|
-
const market = markets.find(m => m.symbol
|
|
40
|
-
if (market)
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
log(`[DCA] Price $${price.toFixed(2)} < limit $${params.priceLimit}. Skipping.`);
|
|
49
|
-
await sleep(intervalSec * 1000);
|
|
50
|
-
continue;
|
|
51
|
-
}
|
|
55
|
+
const market = markets.find(m => symbolMatch(m.symbol, symbol));
|
|
56
|
+
if (market === undefined)
|
|
57
|
+
why = `${symbol} not listed on ${adapter.name}`;
|
|
58
|
+
else {
|
|
59
|
+
const p = parseFloat(market.markPrice);
|
|
60
|
+
if (Number.isFinite(p) && p > 0)
|
|
61
|
+
price = p;
|
|
62
|
+
else
|
|
63
|
+
why = `mark price "${market.markPrice}" is not a price`;
|
|
52
64
|
}
|
|
53
65
|
}
|
|
54
|
-
catch {
|
|
55
|
-
|
|
66
|
+
catch (e) {
|
|
67
|
+
why = `price read failed: ${e instanceof Error ? e.message : String(e)}`;
|
|
68
|
+
}
|
|
69
|
+
const skip = price === null
|
|
70
|
+
? `[DCA] Price unknown (${why}) — cannot check the $${params.priceLimit} limit. Skipping this order.`
|
|
71
|
+
: side === "buy" && price > params.priceLimit ? `[DCA] Price $${price.toFixed(2)} > limit $${params.priceLimit}. Skipping.`
|
|
72
|
+
: side === "sell" && price < params.priceLimit ? `[DCA] Price $${price.toFixed(2)} < limit $${params.priceLimit}. Skipping.`
|
|
73
|
+
: null;
|
|
74
|
+
if (skip !== null) {
|
|
75
|
+
log(skip);
|
|
76
|
+
await sleep(intervalSec * 1000);
|
|
77
|
+
continue;
|
|
56
78
|
}
|
|
57
79
|
}
|
|
58
80
|
// Place market order
|
|
59
81
|
try {
|
|
60
|
-
const result = await adapter.marketOrder(symbol, side, String(amountPerOrder));
|
|
82
|
+
const result = await adapter.marketOrder(symbol, side, String(amountPerOrder), { reduceOnly: false, slippage: resolveAdapterSlippage(adapter, null) });
|
|
61
83
|
state.ordersPlaced++;
|
|
62
|
-
|
|
63
|
-
const
|
|
64
|
-
if (
|
|
65
|
-
|
|
66
|
-
|
|
84
|
+
// Filled size: the venue-reported fill only (IOC outcome / OrderResult.filledSize) — never the size sent.
|
|
85
|
+
const filled = filledOf(adapter, result);
|
|
86
|
+
if (filled === null) {
|
|
87
|
+
pending = pendingStop("dca", adapter, symbol, `DCA ${side} ${amountPerOrder} ${symbol} on ${adapter.name}: accepted but the venue reported no fill size`, log);
|
|
88
|
+
break;
|
|
67
89
|
}
|
|
90
|
+
state.totalFilled = Number(venueRateString(state.totalFilled + filled, "DCA filled total")); // sum without float noise
|
|
91
|
+
// The venue-reported average fill only: the result's `price` is the protection price that was sent.
|
|
92
|
+
const fillPrice = reportedAvgFillPrice(result);
|
|
93
|
+
if (fillPrice === null)
|
|
94
|
+
everyOrderPriced = false;
|
|
95
|
+
else
|
|
96
|
+
state.totalCost += filled * Number(fillPrice);
|
|
97
|
+
state.avgPrice = everyOrderPriced && state.totalFilled > 0 ? Number(formatComputedPrice(state.totalCost / state.totalFilled)) : null;
|
|
68
98
|
const progress = totalOrders > 0 ? ` (${state.ordersPlaced}/${totalOrders})` : "";
|
|
69
|
-
log(`[DCA] Order #${state.ordersPlaced}${progress}: ${side} ${
|
|
99
|
+
log(`[DCA] Order #${state.ordersPlaced}${progress}: ${side} ${filled} ${symbol}${fillPrice !== null ? ` @ $${fillPrice}` : " (fill price not reported)"}${state.avgPrice !== null ? `, avg $${state.avgPrice}` : ""}`);
|
|
70
100
|
// Update job state
|
|
71
101
|
if (jobId) {
|
|
72
102
|
updateJobState(jobId, {
|
|
@@ -81,8 +111,13 @@ export async function runDCA(adapter, params, jobId, log = console.log) {
|
|
|
81
111
|
}
|
|
82
112
|
}
|
|
83
113
|
catch (err) {
|
|
84
|
-
state.errors++;
|
|
85
114
|
const msg = err instanceof Error ? err.message : String(err);
|
|
115
|
+
if (isOutcomeUnknown(err)) {
|
|
116
|
+
// Unknown outcome: never resubmitted — the run stops and the symbol is quarantined.
|
|
117
|
+
pending = pendingStop("dca", adapter, symbol, `DCA ${side} ${amountPerOrder} ${symbol} on ${adapter.name}: outcome unknown (${msg})`, log);
|
|
118
|
+
break;
|
|
119
|
+
}
|
|
120
|
+
state.errors++;
|
|
86
121
|
log(`[DCA] Order error: ${msg}`);
|
|
87
122
|
if (state.errors > 10 && state.errors > state.ordersPlaced) {
|
|
88
123
|
log(`[DCA] Too many errors. Stopping.`);
|
|
@@ -100,7 +135,12 @@ export async function runDCA(adapter, params, jobId, log = console.log) {
|
|
|
100
135
|
process.removeListener("SIGTERM", shutdown);
|
|
101
136
|
}
|
|
102
137
|
const runtime = Math.floor((Date.now() - state.startedAt) / 1000);
|
|
103
|
-
log(`[DCA] Done. ${state.ordersPlaced} orders, ${state.totalFilled} filled, avg
|
|
138
|
+
log(`[DCA] Done. ${state.ordersPlaced} orders, ${state.totalFilled} filled, avg ${state.avgPrice !== null ? `$${state.avgPrice}` : "not reported by the venue"}, ${state.errors} errors, ${runtime}s`);
|
|
139
|
+
if (pending !== null) {
|
|
140
|
+
if (jobId)
|
|
141
|
+
updateJobState(jobId, { status: "error", result: { error: pending.message, ordersPlaced: state.ordersPlaced, totalFilled: state.totalFilled, avgPrice: state.avgPrice, runtime } });
|
|
142
|
+
throw pending;
|
|
143
|
+
}
|
|
104
144
|
if (jobId) {
|
|
105
145
|
updateJobState(jobId, {
|
|
106
146
|
status: "done",
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { type ExchangeAdapter } from "../exchanges/index.js";
|
|
2
2
|
export interface FundingArbParams {
|
|
3
3
|
minSpread: number;
|
|
4
4
|
closeSpread: number;
|
|
@@ -12,4 +12,14 @@ export interface FundingArbParams {
|
|
|
12
12
|
rebalanceThreshold?: number;
|
|
13
13
|
maxDrawdown?: number;
|
|
14
14
|
}
|
|
15
|
+
/**
|
|
16
|
+
* `perp strategy funding-arb` loop. With autoExecute (design §12.3):
|
|
17
|
+
* - entry = openPerpPairVerified (long, then the short sized to the long's VERIFIED fill;
|
|
18
|
+
* definite short failure → reduce-only rollback; unknown outcome → quarantine). A lot-size
|
|
19
|
+
* rejection is never resubmitted blindly: the long is re-sized only when it was definitely
|
|
20
|
+
* not placed; a rejected short is placed once at the lot-floored verified long fill;
|
|
21
|
+
* - close / max-drawdown close = closePerpPairVerified with the venue position size strings
|
|
22
|
+
* (reduce-only, verified, one residual close, quarantine on unknown / unhedged);
|
|
23
|
+
* - quarantined symbols (src/bot/quarantine.ts) are never entered or closed.
|
|
24
|
+
*/
|
|
15
25
|
export declare function runFundingArb(adapters: Map<string, ExchangeAdapter>, params: FundingArbParams, jobId?: string, log?: (msg: string) => void): Promise<void>;
|