perp-cli 0.3.3
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/LICENSE +21 -0
- package/README.md +293 -0
- package/dist/__tests__/alert-logic.test.d.ts +1 -0
- package/dist/__tests__/alert-logic.test.js +107 -0
- package/dist/__tests__/arb-auto-3dex.test.d.ts +1 -0
- package/dist/__tests__/arb-auto-3dex.test.js +397 -0
- package/dist/__tests__/arb-history-stats.test.d.ts +1 -0
- package/dist/__tests__/arb-history-stats.test.js +176 -0
- package/dist/__tests__/arb-logic.test.d.ts +1 -0
- package/dist/__tests__/arb-logic.test.js +84 -0
- package/dist/__tests__/arb-manage.test.d.ts +1 -0
- package/dist/__tests__/arb-manage.test.js +253 -0
- package/dist/__tests__/arb-new-features.test.d.ts +1 -0
- package/dist/__tests__/arb-new-features.test.js +457 -0
- package/dist/__tests__/arb-sizing.test.d.ts +1 -0
- package/dist/__tests__/arb-sizing.test.js +48 -0
- package/dist/__tests__/arb-state.test.d.ts +1 -0
- package/dist/__tests__/arb-state.test.js +284 -0
- package/dist/__tests__/arb-userflow.test.d.ts +1 -0
- package/dist/__tests__/arb-userflow.test.js +945 -0
- package/dist/__tests__/arb-utils.test.d.ts +1 -0
- package/dist/__tests__/arb-utils.test.js +264 -0
- package/dist/__tests__/bot-conditions.test.d.ts +1 -0
- package/dist/__tests__/bot-conditions.test.js +341 -0
- package/dist/__tests__/client-id-tracker.test.d.ts +1 -0
- package/dist/__tests__/client-id-tracker.test.js +137 -0
- package/dist/__tests__/commands/new-atomic-commands.test.d.ts +1 -0
- package/dist/__tests__/commands/new-atomic-commands.test.js +502 -0
- package/dist/__tests__/commands/order-intent.test.d.ts +1 -0
- package/dist/__tests__/commands/order-intent.test.js +600 -0
- package/dist/__tests__/commands/trade-commands.test.d.ts +1 -0
- package/dist/__tests__/commands/trade-commands.test.js +821 -0
- package/dist/__tests__/config.test.d.ts +1 -0
- package/dist/__tests__/config.test.js +86 -0
- package/dist/__tests__/cross-chain-margin.test.d.ts +1 -0
- package/dist/__tests__/cross-chain-margin.test.js +287 -0
- package/dist/__tests__/dex-asset-map.test.d.ts +1 -0
- package/dist/__tests__/dex-asset-map.test.js +191 -0
- package/dist/__tests__/errors.test.d.ts +1 -0
- package/dist/__tests__/errors.test.js +110 -0
- package/dist/__tests__/event-stream.test.d.ts +1 -0
- package/dist/__tests__/event-stream.test.js +276 -0
- package/dist/__tests__/exchanges/interface.test.d.ts +1 -0
- package/dist/__tests__/exchanges/interface.test.js +132 -0
- package/dist/__tests__/exchanges/mock-adapter.d.ts +69 -0
- package/dist/__tests__/exchanges/mock-adapter.js +137 -0
- package/dist/__tests__/execution-log.test.d.ts +1 -0
- package/dist/__tests__/execution-log.test.js +106 -0
- package/dist/__tests__/funding-calc.test.d.ts +1 -0
- package/dist/__tests__/funding-calc.test.js +71 -0
- package/dist/__tests__/funding-history.test.d.ts +1 -0
- package/dist/__tests__/funding-history.test.js +343 -0
- package/dist/__tests__/funding-rates.test.d.ts +1 -0
- package/dist/__tests__/funding-rates.test.js +342 -0
- package/dist/__tests__/funding.test.d.ts +1 -0
- package/dist/__tests__/funding.test.js +173 -0
- package/dist/__tests__/gap-logic.test.d.ts +1 -0
- package/dist/__tests__/gap-logic.test.js +43 -0
- package/dist/__tests__/hip3-dex.test.d.ts +1 -0
- package/dist/__tests__/hip3-dex.test.js +234 -0
- package/dist/__tests__/integration/agent-features.integration.test.d.ts +1 -0
- package/dist/__tests__/integration/agent-features.integration.test.js +553 -0
- package/dist/__tests__/integration/atomic-commands.integration.test.d.ts +13 -0
- package/dist/__tests__/integration/atomic-commands.integration.test.js +246 -0
- package/dist/__tests__/integration/bridge-simulation.integration.test.d.ts +1 -0
- package/dist/__tests__/integration/bridge-simulation.integration.test.js +453 -0
- package/dist/__tests__/integration/bridge-strict.integration.test.d.ts +1 -0
- package/dist/__tests__/integration/bridge-strict.integration.test.js +812 -0
- package/dist/__tests__/integration/bridge.integration.test.d.ts +1 -0
- package/dist/__tests__/integration/bridge.integration.test.js +309 -0
- package/dist/__tests__/integration/cli-e2e.integration.test.d.ts +1 -0
- package/dist/__tests__/integration/cli-e2e.integration.test.js +202 -0
- package/dist/__tests__/integration/dex-arb.integration.test.d.ts +1 -0
- package/dist/__tests__/integration/dex-arb.integration.test.js +116 -0
- package/dist/__tests__/integration/envelope-consistency.integration.test.d.ts +13 -0
- package/dist/__tests__/integration/envelope-consistency.integration.test.js +205 -0
- package/dist/__tests__/integration/hip3-dex.integration.test.d.ts +1 -0
- package/dist/__tests__/integration/hip3-dex.integration.test.js +147 -0
- package/dist/__tests__/integration/hyperliquid.integration.test.d.ts +1 -0
- package/dist/__tests__/integration/hyperliquid.integration.test.js +79 -0
- package/dist/__tests__/integration/lighter.integration.test.d.ts +1 -0
- package/dist/__tests__/integration/lighter.integration.test.js +53 -0
- package/dist/__tests__/integration/new-commands-e2e.integration.test.d.ts +9 -0
- package/dist/__tests__/integration/new-commands-e2e.integration.test.js +236 -0
- package/dist/__tests__/integration/order-verification.integration.test.d.ts +1 -0
- package/dist/__tests__/integration/order-verification.integration.test.js +321 -0
- package/dist/__tests__/integration/pacifica.integration.test.d.ts +1 -0
- package/dist/__tests__/integration/pacifica.integration.test.js +75 -0
- package/dist/__tests__/integration/response-shapes.integration.test.d.ts +1 -0
- package/dist/__tests__/integration/response-shapes.integration.test.js +278 -0
- package/dist/__tests__/liquidity.test.d.ts +1 -0
- package/dist/__tests__/liquidity.test.js +225 -0
- package/dist/__tests__/plan-executor.test.d.ts +1 -0
- package/dist/__tests__/plan-executor.test.js +314 -0
- package/dist/__tests__/position-history.test.d.ts +1 -0
- package/dist/__tests__/position-history.test.js +367 -0
- package/dist/__tests__/retry.test.d.ts +1 -0
- package/dist/__tests__/retry.test.js +310 -0
- package/dist/__tests__/risk-assessment.test.d.ts +1 -0
- package/dist/__tests__/risk-assessment.test.js +145 -0
- package/dist/__tests__/security-adversarial.test.d.ts +1 -0
- package/dist/__tests__/security-adversarial.test.js +574 -0
- package/dist/__tests__/strategies.test.d.ts +1 -0
- package/dist/__tests__/strategies.test.js +539 -0
- package/dist/__tests__/trade-execution.test.d.ts +1 -0
- package/dist/__tests__/trade-execution.test.js +129 -0
- package/dist/__tests__/trade-validator.test.d.ts +1 -0
- package/dist/__tests__/trade-validator.test.js +655 -0
- package/dist/__tests__/utils.test.d.ts +1 -0
- package/dist/__tests__/utils.test.js +76 -0
- package/dist/api/public/hyperliquid.d.ts +18 -0
- package/dist/api/public/hyperliquid.js +82 -0
- package/dist/api/public/index.d.ts +8 -0
- package/dist/api/public/index.js +8 -0
- package/dist/api/public/lighter.d.ts +24 -0
- package/dist/api/public/lighter.js +100 -0
- package/dist/api/public/pacifica.d.ts +17 -0
- package/dist/api/public/pacifica.js +54 -0
- package/dist/api/public/urls.d.ts +12 -0
- package/dist/api/public/urls.js +33 -0
- package/dist/arb/history-stats.d.ts +44 -0
- package/dist/arb/history-stats.js +135 -0
- package/dist/arb/index.d.ts +4 -0
- package/dist/arb/index.js +4 -0
- package/dist/arb/sizing.d.ts +23 -0
- package/dist/arb/sizing.js +96 -0
- package/dist/arb/state.d.ts +51 -0
- package/dist/arb/state.js +112 -0
- package/dist/arb/utils.d.ts +81 -0
- package/dist/arb/utils.js +267 -0
- package/dist/arb-history-stats.d.ts +5 -0
- package/dist/arb-history-stats.js +5 -0
- package/dist/arb-sizing.d.ts +5 -0
- package/dist/arb-sizing.js +5 -0
- package/dist/arb-state.d.ts +5 -0
- package/dist/arb-state.js +5 -0
- package/dist/arb-utils.d.ts +5 -0
- package/dist/arb-utils.js +5 -0
- package/dist/bot/conditions.d.ts +32 -0
- package/dist/bot/conditions.js +141 -0
- package/dist/bot/config.d.ts +76 -0
- package/dist/bot/config.js +160 -0
- package/dist/bot/engine.d.ts +8 -0
- package/dist/bot/engine.js +519 -0
- package/dist/bot/presets.d.ts +11 -0
- package/dist/bot/presets.js +296 -0
- package/dist/bridge-engine.d.ts +133 -0
- package/dist/bridge-engine.js +1487 -0
- package/dist/cache.d.ts +25 -0
- package/dist/cache.js +99 -0
- package/dist/cli-spec.d.ts +50 -0
- package/dist/cli-spec.js +75 -0
- package/dist/client-id-tracker.d.ts +25 -0
- package/dist/client-id-tracker.js +76 -0
- package/dist/commands/account.d.ts +3 -0
- package/dist/commands/account.js +425 -0
- package/dist/commands/agent.d.ts +3 -0
- package/dist/commands/agent.js +386 -0
- package/dist/commands/alert.d.ts +2 -0
- package/dist/commands/alert.js +421 -0
- package/dist/commands/analytics.d.ts +3 -0
- package/dist/commands/analytics.js +311 -0
- package/dist/commands/arb/index.d.ts +3 -0
- package/dist/commands/arb/index.js +921 -0
- package/dist/commands/arb-auto.d.ts +54 -0
- package/dist/commands/arb-auto.js +1328 -0
- package/dist/commands/arb-manage.d.ts +5 -0
- package/dist/commands/arb-manage.js +5 -0
- package/dist/commands/arb.d.ts +2 -0
- package/dist/commands/arb.js +347 -0
- package/dist/commands/backtest.d.ts +2 -0
- package/dist/commands/backtest.js +327 -0
- package/dist/commands/bot.d.ts +3 -0
- package/dist/commands/bot.js +412 -0
- package/dist/commands/bridge.d.ts +2 -0
- package/dist/commands/bridge.js +396 -0
- package/dist/commands/dashboard.d.ts +3 -0
- package/dist/commands/dashboard.js +176 -0
- package/dist/commands/deposit.d.ts +4 -0
- package/dist/commands/deposit.js +573 -0
- package/dist/commands/dex.d.ts +3 -0
- package/dist/commands/dex.js +114 -0
- package/dist/commands/env.d.ts +2 -0
- package/dist/commands/env.js +136 -0
- package/dist/commands/funding.d.ts +2 -0
- package/dist/commands/funding.js +347 -0
- package/dist/commands/gap.d.ts +2 -0
- package/dist/commands/gap.js +305 -0
- package/dist/commands/health.d.ts +2 -0
- package/dist/commands/health.js +67 -0
- package/dist/commands/history.d.ts +2 -0
- package/dist/commands/history.js +235 -0
- package/dist/commands/init.d.ts +15 -0
- package/dist/commands/init.js +266 -0
- package/dist/commands/jobs.d.ts +2 -0
- package/dist/commands/jobs.js +133 -0
- package/dist/commands/manage.d.ts +4 -0
- package/dist/commands/manage.js +309 -0
- package/dist/commands/market.d.ts +3 -0
- package/dist/commands/market.js +225 -0
- package/dist/commands/plan.d.ts +3 -0
- package/dist/commands/plan.js +95 -0
- package/dist/commands/portfolio.d.ts +3 -0
- package/dist/commands/portfolio.js +169 -0
- package/dist/commands/rebalance.d.ts +3 -0
- package/dist/commands/rebalance.js +293 -0
- package/dist/commands/risk.d.ts +3 -0
- package/dist/commands/risk.js +169 -0
- package/dist/commands/run.d.ts +3 -0
- package/dist/commands/run.js +202 -0
- package/dist/commands/settings.d.ts +2 -0
- package/dist/commands/settings.js +102 -0
- package/dist/commands/stream.d.ts +5 -0
- package/dist/commands/stream.js +123 -0
- package/dist/commands/trade.d.ts +3 -0
- package/dist/commands/trade.js +1273 -0
- package/dist/commands/wallet.d.ts +14 -0
- package/dist/commands/wallet.js +602 -0
- package/dist/commands/withdraw.d.ts +3 -0
- package/dist/commands/withdraw.js +187 -0
- package/dist/config.d.ts +5 -0
- package/dist/config.js +68 -0
- package/dist/cross-chain-margin.d.ts +46 -0
- package/dist/cross-chain-margin.js +107 -0
- package/dist/dashboard/server.d.ts +80 -0
- package/dist/dashboard/server.js +340 -0
- package/dist/dashboard/ui.d.ts +4 -0
- package/dist/dashboard/ui.js +538 -0
- package/dist/dashboard/ws-feeds.d.ts +29 -0
- package/dist/dashboard/ws-feeds.js +660 -0
- package/dist/dex-asset-map.d.ts +80 -0
- package/dist/dex-asset-map.js +201 -0
- package/dist/errors.d.ts +109 -0
- package/dist/errors.js +84 -0
- package/dist/event-stream.d.ts +25 -0
- package/dist/event-stream.js +168 -0
- package/dist/exchanges/hyperliquid.d.ts +212 -0
- package/dist/exchanges/hyperliquid.js +931 -0
- package/dist/exchanges/interface.d.ts +95 -0
- package/dist/exchanges/interface.js +5 -0
- package/dist/exchanges/lighter.d.ts +159 -0
- package/dist/exchanges/lighter.js +793 -0
- package/dist/exchanges/pacifica.d.ts +51 -0
- package/dist/exchanges/pacifica.js +248 -0
- package/dist/execution-log.d.ts +36 -0
- package/dist/execution-log.js +102 -0
- package/dist/funding/history.d.ts +63 -0
- package/dist/funding/history.js +266 -0
- package/dist/funding/index.d.ts +3 -0
- package/dist/funding/index.js +3 -0
- package/dist/funding/normalize.d.ts +39 -0
- package/dist/funding/normalize.js +66 -0
- package/dist/funding/rates.d.ts +45 -0
- package/dist/funding/rates.js +172 -0
- package/dist/funding-history.d.ts +5 -0
- package/dist/funding-history.js +5 -0
- package/dist/funding-rates.d.ts +5 -0
- package/dist/funding-rates.js +5 -0
- package/dist/funding.d.ts +5 -0
- package/dist/funding.js +5 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +458 -0
- package/dist/jobs.d.ts +37 -0
- package/dist/jobs.js +152 -0
- package/dist/liquidity.d.ts +34 -0
- package/dist/liquidity.js +100 -0
- package/dist/mcp-server.d.ts +9 -0
- package/dist/mcp-server.js +1206 -0
- package/dist/pacifica/client.d.ts +111 -0
- package/dist/pacifica/client.js +310 -0
- package/dist/pacifica/constants.d.ts +27 -0
- package/dist/pacifica/constants.js +47 -0
- package/dist/pacifica/deposit.d.ts +14 -0
- package/dist/pacifica/deposit.js +78 -0
- package/dist/pacifica/index.d.ts +6 -0
- package/dist/pacifica/index.js +11 -0
- package/dist/pacifica/signing.d.ts +49 -0
- package/dist/pacifica/signing.js +97 -0
- package/dist/pacifica/types/account.d.ts +42 -0
- package/dist/pacifica/types/account.js +1 -0
- package/dist/pacifica/types/index.d.ts +6 -0
- package/dist/pacifica/types/index.js +6 -0
- package/dist/pacifica/types/lake.d.ts +18 -0
- package/dist/pacifica/types/lake.js +1 -0
- package/dist/pacifica/types/market.d.ts +64 -0
- package/dist/pacifica/types/market.js +1 -0
- package/dist/pacifica/types/order.d.ts +92 -0
- package/dist/pacifica/types/order.js +1 -0
- package/dist/pacifica/types/position.d.ts +25 -0
- package/dist/pacifica/types/position.js +1 -0
- package/dist/pacifica/types/ws.d.ts +34 -0
- package/dist/pacifica/types/ws.js +41 -0
- package/dist/pacifica/ws-client.d.ts +42 -0
- package/dist/pacifica/ws-client.js +180 -0
- package/dist/plan-executor.d.ts +48 -0
- package/dist/plan-executor.js +280 -0
- package/dist/position-history.d.ts +68 -0
- package/dist/position-history.js +222 -0
- package/dist/rebalance.d.ts +64 -0
- package/dist/rebalance.js +142 -0
- package/dist/retry.d.ts +74 -0
- package/dist/retry.js +129 -0
- package/dist/risk.d.ts +48 -0
- package/dist/risk.js +156 -0
- package/dist/settings.d.ts +19 -0
- package/dist/settings.js +45 -0
- package/dist/shared-api.d.ts +5 -0
- package/dist/shared-api.js +5 -0
- package/dist/strategies/dca.d.ts +25 -0
- package/dist/strategies/dca.js +114 -0
- package/dist/strategies/funding-arb.d.ts +15 -0
- package/dist/strategies/funding-arb.js +281 -0
- package/dist/strategies/grid.d.ts +34 -0
- package/dist/strategies/grid.js +185 -0
- package/dist/strategies/trailing-stop.d.ts +17 -0
- package/dist/strategies/trailing-stop.js +121 -0
- package/dist/strategies/twap.d.ts +20 -0
- package/dist/strategies/twap.js +78 -0
- package/dist/trade-validator.d.ts +39 -0
- package/dist/trade-validator.js +154 -0
- package/dist/utils.d.ts +38 -0
- package/dist/utils.js +110 -0
- package/package.json +63 -0
- package/skills/perp-cli/SKILL.md +149 -0
- package/skills/perp-cli/references/commands.md +143 -0
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { ExchangeAdapter } from "../exchanges/interface.js";
|
|
2
|
+
import type { Condition } from "./config.js";
|
|
3
|
+
export interface MarketSnapshot {
|
|
4
|
+
price: number;
|
|
5
|
+
high24h: number;
|
|
6
|
+
low24h: number;
|
|
7
|
+
volume24h: number;
|
|
8
|
+
fundingRate: number;
|
|
9
|
+
volatility24h: number;
|
|
10
|
+
rsi: number;
|
|
11
|
+
spreadPct: number;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Calculate RSI from an array of closing prices using Wilder's smoothing method.
|
|
15
|
+
* @param closes Array of closing prices, oldest first.
|
|
16
|
+
* @param period RSI period (default 14).
|
|
17
|
+
* @returns RSI value between 0 and 100, or NaN if insufficient data.
|
|
18
|
+
*/
|
|
19
|
+
export declare function calculateRSI(closes: number[], period?: number): number;
|
|
20
|
+
export declare function getMarketSnapshot(adapter: ExchangeAdapter, symbol: string): Promise<MarketSnapshot>;
|
|
21
|
+
export declare function evaluateCondition(condition: Condition, snapshot: MarketSnapshot, context: {
|
|
22
|
+
equity: number;
|
|
23
|
+
startTime: number;
|
|
24
|
+
peakEquity: number;
|
|
25
|
+
dailyPnl: number;
|
|
26
|
+
}): boolean;
|
|
27
|
+
export declare function evaluateAllConditions(conditions: Condition[], snapshot: MarketSnapshot, context: {
|
|
28
|
+
equity: number;
|
|
29
|
+
startTime: number;
|
|
30
|
+
peakEquity: number;
|
|
31
|
+
dailyPnl: number;
|
|
32
|
+
}, mode?: "all" | "any"): boolean;
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
// ── RSI Calculation (Wilder smoothing) ──
|
|
2
|
+
/**
|
|
3
|
+
* Calculate RSI from an array of closing prices using Wilder's smoothing method.
|
|
4
|
+
* @param closes Array of closing prices, oldest first.
|
|
5
|
+
* @param period RSI period (default 14).
|
|
6
|
+
* @returns RSI value between 0 and 100, or NaN if insufficient data.
|
|
7
|
+
*/
|
|
8
|
+
export function calculateRSI(closes, period = 14) {
|
|
9
|
+
if (closes.length < period + 1)
|
|
10
|
+
return NaN;
|
|
11
|
+
// Calculate price changes (deltas)
|
|
12
|
+
const deltas = [];
|
|
13
|
+
for (let i = 1; i < closes.length; i++) {
|
|
14
|
+
deltas.push(closes[i] - closes[i - 1]);
|
|
15
|
+
}
|
|
16
|
+
// Separate gains and losses
|
|
17
|
+
const gains = deltas.map(d => (d > 0 ? d : 0));
|
|
18
|
+
const losses = deltas.map(d => (d < 0 ? -d : 0));
|
|
19
|
+
// First average: simple mean of first `period` values
|
|
20
|
+
let avgGain = 0;
|
|
21
|
+
let avgLoss = 0;
|
|
22
|
+
for (let i = 0; i < period; i++) {
|
|
23
|
+
avgGain += gains[i];
|
|
24
|
+
avgLoss += losses[i];
|
|
25
|
+
}
|
|
26
|
+
avgGain /= period;
|
|
27
|
+
avgLoss /= period;
|
|
28
|
+
// Subsequent averages: Wilder smoothing
|
|
29
|
+
for (let i = period; i < deltas.length; i++) {
|
|
30
|
+
avgGain = (avgGain * (period - 1) + gains[i]) / period;
|
|
31
|
+
avgLoss = (avgLoss * (period - 1) + losses[i]) / period;
|
|
32
|
+
}
|
|
33
|
+
// Handle edge case: no losses at all
|
|
34
|
+
if (avgLoss === 0) {
|
|
35
|
+
return avgGain === 0 ? 50 : 100;
|
|
36
|
+
}
|
|
37
|
+
const rs = avgGain / avgLoss;
|
|
38
|
+
return 100 - 100 / (1 + rs);
|
|
39
|
+
}
|
|
40
|
+
export async function getMarketSnapshot(adapter, symbol) {
|
|
41
|
+
const markets = await adapter.getMarkets();
|
|
42
|
+
const m = markets.find(mk => mk.symbol.toUpperCase() === symbol.toUpperCase());
|
|
43
|
+
if (!m) {
|
|
44
|
+
return { price: 0, high24h: 0, low24h: 0, volume24h: 0, fundingRate: 0, volatility24h: 0, rsi: NaN, spreadPct: 0 };
|
|
45
|
+
}
|
|
46
|
+
const price = parseFloat(m.markPrice);
|
|
47
|
+
const fundingRate = parseFloat(m.fundingRate);
|
|
48
|
+
const volume24h = parseFloat(m.volume24h);
|
|
49
|
+
// Try to get kline data for 24h high/low + RSI
|
|
50
|
+
let high24h = price;
|
|
51
|
+
let low24h = price;
|
|
52
|
+
let rsi = NaN;
|
|
53
|
+
try {
|
|
54
|
+
const now = Date.now();
|
|
55
|
+
// Fetch enough candles for RSI (need period+1 candles minimum; fetch 48h for safety)
|
|
56
|
+
const klines = await adapter.getKlines(symbol, "1h", now - 48 * 3600 * 1000, now);
|
|
57
|
+
if (klines.length > 0) {
|
|
58
|
+
// Sort by time ascending
|
|
59
|
+
klines.sort((a, b) => a.time - b.time);
|
|
60
|
+
// 24h high/low: use only the last 24 candles
|
|
61
|
+
const last24h = klines.filter(k => k.time >= now - 24 * 3600 * 1000);
|
|
62
|
+
if (last24h.length > 0) {
|
|
63
|
+
high24h = Math.max(...last24h.map(k => parseFloat(k.high)));
|
|
64
|
+
low24h = Math.min(...last24h.map(k => parseFloat(k.low)));
|
|
65
|
+
}
|
|
66
|
+
// RSI from all available close prices
|
|
67
|
+
const closes = klines.map(k => parseFloat(k.close));
|
|
68
|
+
rsi = calculateRSI(closes, 14);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
catch {
|
|
72
|
+
// non-critical
|
|
73
|
+
}
|
|
74
|
+
// Calculate bid-ask spread from orderbook
|
|
75
|
+
let spreadPct = 0;
|
|
76
|
+
try {
|
|
77
|
+
const ob = await adapter.getOrderbook(symbol);
|
|
78
|
+
if (ob.bids.length > 0 && ob.asks.length > 0) {
|
|
79
|
+
const bestBid = parseFloat(ob.bids[0][0]);
|
|
80
|
+
const bestAsk = parseFloat(ob.asks[0][0]);
|
|
81
|
+
const midPrice = (bestBid + bestAsk) / 2;
|
|
82
|
+
if (midPrice > 0) {
|
|
83
|
+
spreadPct = ((bestAsk - bestBid) / midPrice) * 100;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
catch {
|
|
88
|
+
// non-critical
|
|
89
|
+
}
|
|
90
|
+
const volatility24h = price > 0 ? ((high24h - low24h) / price) * 100 : 0;
|
|
91
|
+
return { price, high24h, low24h, volume24h, fundingRate, volatility24h, rsi, spreadPct };
|
|
92
|
+
}
|
|
93
|
+
export function evaluateCondition(condition, snapshot, context) {
|
|
94
|
+
const val = typeof condition.value === "number" ? condition.value : parseFloat(String(condition.value));
|
|
95
|
+
switch (condition.type) {
|
|
96
|
+
case "always":
|
|
97
|
+
return true;
|
|
98
|
+
case "price_above":
|
|
99
|
+
return snapshot.price > val;
|
|
100
|
+
case "price_below":
|
|
101
|
+
return snapshot.price < val;
|
|
102
|
+
case "volatility_above":
|
|
103
|
+
return snapshot.volatility24h > val;
|
|
104
|
+
case "volatility_below":
|
|
105
|
+
return snapshot.volatility24h < val;
|
|
106
|
+
case "funding_rate_above":
|
|
107
|
+
return snapshot.fundingRate > val;
|
|
108
|
+
case "funding_rate_below":
|
|
109
|
+
return snapshot.fundingRate < val;
|
|
110
|
+
case "rsi_above":
|
|
111
|
+
// RSI was pre-computed in the snapshot from 1h kline data.
|
|
112
|
+
// If RSI is NaN (insufficient data), the condition is not met.
|
|
113
|
+
return !isNaN(snapshot.rsi) && snapshot.rsi > val;
|
|
114
|
+
case "rsi_below":
|
|
115
|
+
return !isNaN(snapshot.rsi) && snapshot.rsi < val;
|
|
116
|
+
case "balance_above":
|
|
117
|
+
return context.equity > val;
|
|
118
|
+
case "balance_below":
|
|
119
|
+
return context.equity < val;
|
|
120
|
+
case "spread_above":
|
|
121
|
+
// Bid-ask spread percentage, pre-computed from orderbook in snapshot.
|
|
122
|
+
return snapshot.spreadPct > val;
|
|
123
|
+
case "time_after": {
|
|
124
|
+
const elapsed = (Date.now() - context.startTime) / 1000;
|
|
125
|
+
return elapsed > val;
|
|
126
|
+
}
|
|
127
|
+
// Custom exit conditions (not standard ConditionType but used in exit logic)
|
|
128
|
+
case "max_drawdown":
|
|
129
|
+
return (context.peakEquity - context.equity) > val;
|
|
130
|
+
default:
|
|
131
|
+
return false;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
export function evaluateAllConditions(conditions, snapshot, context, mode = "all") {
|
|
135
|
+
if (conditions.length === 0)
|
|
136
|
+
return true;
|
|
137
|
+
if (mode === "all") {
|
|
138
|
+
return conditions.every(c => evaluateCondition(c, snapshot, context));
|
|
139
|
+
}
|
|
140
|
+
return conditions.some(c => evaluateCondition(c, snapshot, context));
|
|
141
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
export type ConditionType = "price_above" | "price_below" | "volatility_above" | "volatility_below" | "funding_rate_above" | "funding_rate_below" | "spread_above" | "rsi_above" | "rsi_below" | "balance_above" | "balance_below" | "time_after" | "always";
|
|
2
|
+
export interface Condition {
|
|
3
|
+
type: ConditionType;
|
|
4
|
+
value: number | string;
|
|
5
|
+
symbol?: string;
|
|
6
|
+
exchange?: string;
|
|
7
|
+
}
|
|
8
|
+
export interface RiskConfig {
|
|
9
|
+
max_position_usd: number;
|
|
10
|
+
max_daily_loss: number;
|
|
11
|
+
max_drawdown: number;
|
|
12
|
+
pause_after_loss_sec: number;
|
|
13
|
+
max_open_bots: number;
|
|
14
|
+
}
|
|
15
|
+
export interface GridStrategyParams {
|
|
16
|
+
type: "grid";
|
|
17
|
+
grids: number;
|
|
18
|
+
size: number;
|
|
19
|
+
side: "long" | "short" | "neutral";
|
|
20
|
+
range_mode: "auto" | "fixed";
|
|
21
|
+
range_pct?: number;
|
|
22
|
+
upper?: number;
|
|
23
|
+
lower?: number;
|
|
24
|
+
rebalance: boolean;
|
|
25
|
+
rebalance_cooldown: number;
|
|
26
|
+
leverage?: number;
|
|
27
|
+
}
|
|
28
|
+
export interface DCAStrategyParams {
|
|
29
|
+
type: "dca";
|
|
30
|
+
amount: number;
|
|
31
|
+
interval_sec: number;
|
|
32
|
+
total_orders: number;
|
|
33
|
+
price_limit?: number;
|
|
34
|
+
}
|
|
35
|
+
export interface FundingArbStrategyParams {
|
|
36
|
+
type: "funding-arb";
|
|
37
|
+
min_spread: number;
|
|
38
|
+
close_spread: number;
|
|
39
|
+
size_usd: number;
|
|
40
|
+
max_positions: number;
|
|
41
|
+
exchanges: string[];
|
|
42
|
+
}
|
|
43
|
+
export type StrategyParams = GridStrategyParams | DCAStrategyParams | FundingArbStrategyParams;
|
|
44
|
+
export interface BotConfig {
|
|
45
|
+
name: string;
|
|
46
|
+
exchange: string;
|
|
47
|
+
symbol: string;
|
|
48
|
+
strategy: StrategyParams;
|
|
49
|
+
entry_conditions: Condition[];
|
|
50
|
+
exit_conditions: Condition[];
|
|
51
|
+
risk: RiskConfig;
|
|
52
|
+
monitor_interval_sec: number;
|
|
53
|
+
}
|
|
54
|
+
export declare function loadBotConfig(pathOrInline: string): BotConfig;
|
|
55
|
+
export declare function quickGridConfig(opts: {
|
|
56
|
+
exchange: string;
|
|
57
|
+
symbol: string;
|
|
58
|
+
rangePct: number;
|
|
59
|
+
grids: number;
|
|
60
|
+
size: number;
|
|
61
|
+
side: string;
|
|
62
|
+
maxDrawdown: number;
|
|
63
|
+
maxRuntime?: number;
|
|
64
|
+
leverage?: number;
|
|
65
|
+
}): BotConfig;
|
|
66
|
+
export declare function quickDCAConfig(opts: {
|
|
67
|
+
exchange: string;
|
|
68
|
+
symbol: string;
|
|
69
|
+
side: string;
|
|
70
|
+
amount: number;
|
|
71
|
+
intervalSec: number;
|
|
72
|
+
orders: number;
|
|
73
|
+
triggerDrop?: number;
|
|
74
|
+
priceLimit?: number;
|
|
75
|
+
maxDrawdown: number;
|
|
76
|
+
}): BotConfig;
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
import { readFileSync, existsSync } from "node:fs";
|
|
2
|
+
import { parse as parseYaml } from "yaml";
|
|
3
|
+
// ── Defaults ──
|
|
4
|
+
const DEFAULT_RISK = {
|
|
5
|
+
max_position_usd: 1000,
|
|
6
|
+
max_daily_loss: 100,
|
|
7
|
+
max_drawdown: 200,
|
|
8
|
+
pause_after_loss_sec: 300,
|
|
9
|
+
max_open_bots: 5,
|
|
10
|
+
};
|
|
11
|
+
// ── Loader ──
|
|
12
|
+
export function loadBotConfig(pathOrInline) {
|
|
13
|
+
let raw;
|
|
14
|
+
if (existsSync(pathOrInline)) {
|
|
15
|
+
const content = readFileSync(pathOrInline, "utf-8");
|
|
16
|
+
if (pathOrInline.endsWith(".json")) {
|
|
17
|
+
raw = JSON.parse(content);
|
|
18
|
+
}
|
|
19
|
+
else {
|
|
20
|
+
raw = parseYaml(content);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
else {
|
|
24
|
+
// Try parsing as inline JSON
|
|
25
|
+
raw = JSON.parse(pathOrInline);
|
|
26
|
+
}
|
|
27
|
+
return parseBotConfig(raw);
|
|
28
|
+
}
|
|
29
|
+
function parseBotConfig(raw) {
|
|
30
|
+
const name = String(raw.name ?? `bot-${Date.now()}`);
|
|
31
|
+
const exchange = String(raw.exchange ?? "hyperliquid");
|
|
32
|
+
const symbol = String(raw.symbol ?? "ETH");
|
|
33
|
+
const monitorInterval = Number(raw.monitor_interval_sec ?? raw.interval ?? 10);
|
|
34
|
+
// Strategy
|
|
35
|
+
const stratRaw = (raw.strategy ?? raw.params ?? {});
|
|
36
|
+
const stratType = String(stratRaw.type ?? raw.strategy_type ?? "grid");
|
|
37
|
+
const strategy = parseStrategy(stratType, stratRaw);
|
|
38
|
+
// Conditions
|
|
39
|
+
const entryConds = parseConditions(raw.entry_conditions);
|
|
40
|
+
const exitConds = parseConditions(raw.exit_conditions);
|
|
41
|
+
// If no entry conditions, default to "always"
|
|
42
|
+
if (entryConds.length === 0) {
|
|
43
|
+
entryConds.push({ type: "always", value: 0 });
|
|
44
|
+
}
|
|
45
|
+
// Risk
|
|
46
|
+
const riskRaw = (raw.risk ?? {});
|
|
47
|
+
const risk = {
|
|
48
|
+
max_position_usd: Number(riskRaw.max_position_usd ?? DEFAULT_RISK.max_position_usd),
|
|
49
|
+
max_daily_loss: Number(riskRaw.max_daily_loss ?? DEFAULT_RISK.max_daily_loss),
|
|
50
|
+
max_drawdown: Number(riskRaw.max_drawdown ?? DEFAULT_RISK.max_drawdown),
|
|
51
|
+
pause_after_loss_sec: Number(riskRaw.pause_after_loss_sec ?? riskRaw.pause_after_loss ?? DEFAULT_RISK.pause_after_loss_sec),
|
|
52
|
+
max_open_bots: Number(riskRaw.max_open_bots ?? DEFAULT_RISK.max_open_bots),
|
|
53
|
+
};
|
|
54
|
+
return { name, exchange, symbol, strategy, entry_conditions: entryConds, exit_conditions: exitConds, risk, monitor_interval_sec: monitorInterval };
|
|
55
|
+
}
|
|
56
|
+
function parseStrategy(type, raw) {
|
|
57
|
+
switch (type) {
|
|
58
|
+
case "grid":
|
|
59
|
+
return {
|
|
60
|
+
type: "grid",
|
|
61
|
+
grids: Number(raw.grids ?? 10),
|
|
62
|
+
size: Number(raw.size ?? 0.1),
|
|
63
|
+
side: (String(raw.side ?? "neutral")),
|
|
64
|
+
range_mode: String(raw.range_mode ?? "auto"),
|
|
65
|
+
range_pct: raw.range_pct !== undefined ? Number(raw.range_pct) : 3,
|
|
66
|
+
upper: raw.upper !== undefined ? Number(raw.upper) : undefined,
|
|
67
|
+
lower: raw.lower !== undefined ? Number(raw.lower) : undefined,
|
|
68
|
+
rebalance: raw.rebalance !== false,
|
|
69
|
+
rebalance_cooldown: Number(raw.rebalance_cooldown ?? 60),
|
|
70
|
+
leverage: raw.leverage !== undefined ? Number(raw.leverage) : undefined,
|
|
71
|
+
};
|
|
72
|
+
case "dca":
|
|
73
|
+
return {
|
|
74
|
+
type: "dca",
|
|
75
|
+
amount: Number(raw.amount ?? 0.01),
|
|
76
|
+
interval_sec: Number(raw.interval_sec ?? raw.interval ?? 60),
|
|
77
|
+
total_orders: Number(raw.total_orders ?? raw.orders ?? 0),
|
|
78
|
+
price_limit: raw.price_limit !== undefined ? Number(raw.price_limit) : undefined,
|
|
79
|
+
};
|
|
80
|
+
case "funding-arb":
|
|
81
|
+
return {
|
|
82
|
+
type: "funding-arb",
|
|
83
|
+
min_spread: Number(raw.min_spread ?? 20),
|
|
84
|
+
close_spread: Number(raw.close_spread ?? 5),
|
|
85
|
+
size_usd: Number(raw.size_usd ?? raw.size ?? 50),
|
|
86
|
+
max_positions: Number(raw.max_positions ?? 3),
|
|
87
|
+
exchanges: Array.isArray(raw.exchanges)
|
|
88
|
+
? raw.exchanges.map(String)
|
|
89
|
+
: String(raw.exchanges ?? "pacifica,hyperliquid").split(",").map(s => s.trim()),
|
|
90
|
+
};
|
|
91
|
+
default:
|
|
92
|
+
throw new Error(`Unknown strategy type: ${type}`);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
function parseConditions(raw) {
|
|
96
|
+
if (!raw || !Array.isArray(raw))
|
|
97
|
+
return [];
|
|
98
|
+
return raw.map((c) => {
|
|
99
|
+
const cond = c;
|
|
100
|
+
return {
|
|
101
|
+
type: String(cond.type ?? "always"),
|
|
102
|
+
value: cond.value !== undefined ? (typeof cond.value === "number" ? cond.value : String(cond.value)) : 0,
|
|
103
|
+
symbol: cond.symbol ? String(cond.symbol) : undefined,
|
|
104
|
+
exchange: cond.exchange ? String(cond.exchange) : undefined,
|
|
105
|
+
};
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
// ── Quick config builders ──
|
|
109
|
+
export function quickGridConfig(opts) {
|
|
110
|
+
return {
|
|
111
|
+
name: `grid-${opts.symbol.toLowerCase()}-${Date.now().toString(36)}`,
|
|
112
|
+
exchange: opts.exchange,
|
|
113
|
+
symbol: opts.symbol,
|
|
114
|
+
strategy: {
|
|
115
|
+
type: "grid",
|
|
116
|
+
grids: opts.grids,
|
|
117
|
+
size: opts.size,
|
|
118
|
+
side: opts.side,
|
|
119
|
+
range_mode: "auto",
|
|
120
|
+
range_pct: opts.rangePct,
|
|
121
|
+
rebalance: true,
|
|
122
|
+
rebalance_cooldown: 60,
|
|
123
|
+
leverage: opts.leverage,
|
|
124
|
+
},
|
|
125
|
+
entry_conditions: [{ type: "always", value: 0 }],
|
|
126
|
+
exit_conditions: [
|
|
127
|
+
{ type: "max_drawdown", value: opts.maxDrawdown },
|
|
128
|
+
...(opts.maxRuntime ? [{ type: "time_after", value: opts.maxRuntime }] : []),
|
|
129
|
+
],
|
|
130
|
+
risk: { ...DEFAULT_RISK, max_drawdown: opts.maxDrawdown },
|
|
131
|
+
monitor_interval_sec: 10,
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
export function quickDCAConfig(opts) {
|
|
135
|
+
const entryConds = [];
|
|
136
|
+
if (opts.triggerDrop) {
|
|
137
|
+
entryConds.push({ type: "price_below", value: opts.triggerDrop });
|
|
138
|
+
}
|
|
139
|
+
else {
|
|
140
|
+
entryConds.push({ type: "always", value: 0 });
|
|
141
|
+
}
|
|
142
|
+
return {
|
|
143
|
+
name: `dca-${opts.symbol.toLowerCase()}-${Date.now().toString(36)}`,
|
|
144
|
+
exchange: opts.exchange,
|
|
145
|
+
symbol: opts.symbol,
|
|
146
|
+
strategy: {
|
|
147
|
+
type: "dca",
|
|
148
|
+
amount: opts.amount,
|
|
149
|
+
interval_sec: opts.intervalSec,
|
|
150
|
+
total_orders: opts.orders,
|
|
151
|
+
price_limit: opts.priceLimit,
|
|
152
|
+
},
|
|
153
|
+
entry_conditions: entryConds,
|
|
154
|
+
exit_conditions: [
|
|
155
|
+
{ type: "max_drawdown", value: opts.maxDrawdown },
|
|
156
|
+
],
|
|
157
|
+
risk: { ...DEFAULT_RISK, max_drawdown: opts.maxDrawdown },
|
|
158
|
+
monitor_interval_sec: opts.intervalSec,
|
|
159
|
+
};
|
|
160
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { ExchangeAdapter } from "../exchanges/interface.js";
|
|
2
|
+
import type { BotConfig } from "./config.js";
|
|
3
|
+
export type BotLog = (msg: string) => void;
|
|
4
|
+
export declare function runBot(adapter: ExchangeAdapter, config: BotConfig, jobId?: string, log?: BotLog, extraAdapters?: Map<string, ExchangeAdapter>): Promise<{
|
|
5
|
+
fills: number;
|
|
6
|
+
totalPnl: number;
|
|
7
|
+
runtime: number;
|
|
8
|
+
}>;
|