@alphafox/cli 0.1.5 → 0.3.2
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 +42 -4
- package/dist/auth/browser-login.d.ts +30 -0
- package/dist/auth/browser-login.js +193 -0
- package/dist/auth/loopback-callback.d.ts +32 -0
- package/dist/auth/loopback-callback.js +175 -0
- package/dist/auth/open-browser.d.ts +15 -0
- package/dist/auth/open-browser.js +54 -0
- package/dist/auth/refresh.d.ts +27 -2
- package/dist/auth/refresh.js +52 -15
- package/dist/catalog/allowlist.d.ts +20 -4
- package/dist/catalog/allowlist.js +126 -25
- package/dist/catalog/command-tree.d.ts +35 -0
- package/dist/catalog/command-tree.js +119 -0
- package/dist/catalog/compatibility.d.ts +23 -0
- package/dist/catalog/compatibility.js +58 -0
- package/dist/catalog/generated/registry.json +6137 -0
- package/dist/catalog/generated/schemas.json +31036 -0
- package/dist/catalog/operations.d.ts +76 -3
- package/dist/catalog/operations.js +87 -213
- package/dist/catalog/validate-body.d.ts +22 -0
- package/dist/catalog/validate-body.js +98 -0
- package/dist/commands/request-body.d.ts +21 -0
- package/dist/commands/request-body.js +92 -0
- package/dist/commands/run.js +283 -162
- package/dist/config/profiles.js +3 -3
- package/dist/engine-backtest/errors.d.ts +18 -0
- package/dist/engine-backtest/errors.js +26 -0
- package/dist/engine-backtest/fetch-runtime.d.ts +38 -0
- package/dist/engine-backtest/fetch-runtime.js +170 -0
- package/dist/engine-backtest/native-import.d.ts +1 -0
- package/dist/engine-backtest/native-import.js +10 -0
- package/dist/engine-backtest/parse-args.d.ts +6 -0
- package/dist/engine-backtest/parse-args.js +281 -0
- package/dist/engine-backtest/persist.d.ts +32 -0
- package/dist/engine-backtest/persist.js +94 -0
- package/dist/engine-backtest/replay-timeframe.d.ts +16 -0
- package/dist/engine-backtest/replay-timeframe.js +48 -0
- package/dist/engine-backtest/resolve-packages.d.ts +27 -0
- package/dist/engine-backtest/resolve-packages.js +288 -0
- package/dist/engine-backtest/run-command.d.ts +38 -0
- package/dist/engine-backtest/run-command.js +540 -0
- package/dist/engine-backtest/types.d.ts +249 -0
- package/dist/engine-backtest/types.js +2 -0
- package/dist/envelope.d.ts +3 -0
- package/dist/envelope.js +43 -3
- package/dist/http/client.js +18 -15
- package/dist/index.d.ts +16 -5
- package/dist/index.js +47 -1
- package/dist/install/exec.d.ts +13 -0
- package/dist/install/exec.js +73 -0
- package/dist/install/package-root.d.ts +4 -0
- package/dist/install/package-root.js +59 -0
- package/dist/install/types.d.ts +69 -0
- package/dist/install/types.js +26 -0
- package/dist/install/wizard.d.ts +21 -0
- package/dist/install/wizard.js +332 -0
- package/dist/keychain/linux-secret-service.d.ts +11 -0
- package/dist/keychain/linux-secret-service.js +93 -0
- package/dist/keychain/store.d.ts +20 -1
- package/dist/keychain/store.js +94 -6
- package/dist/keychain/windows-credential.d.ts +13 -0
- package/dist/keychain/windows-credential.js +176 -0
- package/dist/safety/confirmation.d.ts +10 -3
- package/dist/safety/confirmation.js +27 -4
- package/dist/version.d.ts +2 -2
- package/dist/version.js +3 -2
- package/docs/.nojekyll +0 -0
- package/docs/agents/domain.md +51 -0
- package/docs/agents/issue-tracker.md +156 -0
- package/docs/agents/triage-labels.md +18 -0
- package/docs/alphafox-cli-installation-guide.md +105 -0
- package/docs/e2e-staging.md +76 -6
- package/docs/favicon.svg +4 -0
- package/docs/index.html +748 -0
- package/docs/logo/alphafox-mark.svg +4 -0
- package/docs/logo/alphafox-wordmark-black-en.svg +17 -0
- package/docs/logo/alphafox-wordmark-white-en.svg +16 -0
- package/docs/release-supply-chain.md +107 -26
- package/package.json +10 -3
- package/skills/account/SKILL.md +8 -6
- package/skills/admin/SKILL.md +9 -4
- package/skills/alphafox-shared/SKILL.md +44 -14
- package/skills/auth/SKILL.md +21 -6
- package/skills/engine-backtest/SKILL.md +71 -0
- package/skills/exchange/SKILL.md +10 -3
- package/skills/market/SKILL.md +9 -4
- package/skills/notification/SKILL.md +8 -3
- package/skills/strategy/SKILL.md +23 -9
- package/skills/trading/SKILL.md +18 -5
- package/vendor/backtest-runner/NOTICE.md +1 -0
- package/vendor/backtest-runner/README.md +97 -0
- package/vendor/backtest-runner/index.d.ts +423 -0
- package/vendor/backtest-runner/index.mjs +59 -0
- package/vendor/backtest-runner/lib/abortable.mjs +23 -0
- package/vendor/backtest-runner/lib/cache.mjs +159 -0
- package/vendor/backtest-runner/lib/coverage.mjs +238 -0
- package/vendor/backtest-runner/lib/encode.mjs +28 -0
- package/vendor/backtest-runner/lib/exchanges.mjs +143 -0
- package/vendor/backtest-runner/lib/proxy.mjs +78 -0
- package/vendor/backtest-runner/lib/scenario.mjs +66 -0
- package/vendor/backtest-runner/lib/series.mjs +370 -0
- package/vendor/backtest-runner/lib/tape-loader.mjs +614 -0
- package/vendor/backtest-runner/lib/timeframes.mjs +55 -0
- package/vendor/backtest-runner/package.json +13 -0
package/skills/strategy/SKILL.md
CHANGED
|
@@ -1,35 +1,49 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: alphafox-strategy
|
|
3
3
|
description: Strategy definitions, chats, and backtests via public operationIds.
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.3.2
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
# Strategy / Chat / Backtest
|
|
8
8
|
|
|
9
|
+
Always `--format json --no-input`. Read scopes `trading:read` / `backtests:read`; writes `chats:write` / `backtests:write`.
|
|
10
|
+
|
|
9
11
|
## Read
|
|
10
12
|
|
|
11
13
|
```bash
|
|
12
|
-
alphafox schema trading.strategy_definitions.list
|
|
13
|
-
alphafox api GET /api/v1/trading/strategy-definitions
|
|
14
|
+
alphafox schema trading.strategy_definitions.list --format json --no-input
|
|
15
|
+
alphafox api GET /api/v1/trading/strategy-definitions --format json --no-input
|
|
14
16
|
```
|
|
15
17
|
|
|
16
18
|
## Write (ordinary)
|
|
17
19
|
|
|
20
|
+
Read `request.body` first. Do not invent chat or strategy fields.
|
|
21
|
+
|
|
18
22
|
```bash
|
|
19
|
-
alphafox
|
|
23
|
+
alphafox schema chats.create --format json --no-input
|
|
24
|
+
alphafox chats create --body '{"strategyGenerationMode":"simple"}' --format json --no-input
|
|
25
|
+
# large / nested bodies:
|
|
26
|
+
# alphafox chats create --config @./create-chat.json --format json --no-input
|
|
20
27
|
```
|
|
21
28
|
|
|
22
|
-
Requires auth. Sends `Idempotency-Key` when available.
|
|
29
|
+
Requires auth. Sends `Idempotency-Key` when available. Duplicate key → `409`; do not invent a new key unless the operator asks to create another chat.
|
|
30
|
+
|
|
31
|
+
Engine strategy backtest (WASM tape + persist) is `skills/engine-backtest` (`alphafox engine-backtest run`). Do not treat `/api/v1/backtests` as the Engine WASM runner — that stub is the chat backtest job below.
|
|
23
32
|
|
|
24
33
|
## Long-running backtest
|
|
25
34
|
|
|
26
35
|
```bash
|
|
27
|
-
alphafox
|
|
28
|
-
alphafox api
|
|
29
|
-
alphafox api GET /api/v1/backtests/{backtestId}
|
|
30
|
-
alphafox api
|
|
36
|
+
alphafox schema backtests.create --format json --no-input
|
|
37
|
+
alphafox api POST /api/v1/backtests --body '{"chatId":"<chat-id>"}' --format json --no-input
|
|
38
|
+
alphafox api GET /api/v1/backtests/{backtestId} --format json --no-input
|
|
39
|
+
alphafox api GET /api/v1/backtests/{backtestId}/stream --format jsonl --no-input
|
|
40
|
+
alphafox api POST /api/v1/backtests/{backtestId}/cancel --body '{}' --format json --no-input
|
|
31
41
|
```
|
|
32
42
|
|
|
43
|
+
`backtests.create` requires `chatId`. `strategyId` is optional: the facade resolves a compiled strategy on that chat. A chat with no compiled strategy returns `CHAT_HAS_NO_COMPILED_STRATEGY` (not `JOB_NOT_FOUND`). Optional `backtestSettings` overlays chat settings when it is a settingsJson object.
|
|
44
|
+
|
|
45
|
+
If the stream drops, `GET` the backtest by id — do not assume success. Cancel is explicit; interruption must remain queryable.
|
|
46
|
+
|
|
33
47
|
## operationIds
|
|
34
48
|
|
|
35
49
|
- `trading.strategy_definitions.list`
|
package/skills/trading/SKILL.md
CHANGED
|
@@ -1,25 +1,38 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: alphafox-trading
|
|
3
3
|
description: Traders list and high-risk start/stop with confirmation gates.
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.3.2
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
# Trading
|
|
8
8
|
|
|
9
|
+
Always `--format json --no-input`. Read first. Writes need scopes `trading:write`; start/stop also `trading:high-risk`.
|
|
10
|
+
|
|
9
11
|
## Read
|
|
10
12
|
|
|
11
13
|
```bash
|
|
12
|
-
alphafox api GET /api/v1/trading/traders
|
|
14
|
+
alphafox api GET /api/v1/trading/traders --format json --no-input
|
|
13
15
|
```
|
|
14
16
|
|
|
15
17
|
## High-risk write
|
|
16
18
|
|
|
19
|
+
Read `alphafox schema trading.traders.byId.start` first. Body may only include documented fields (`reason` is optional). Do not invent keys.
|
|
20
|
+
|
|
17
21
|
```bash
|
|
18
|
-
alphafox
|
|
19
|
-
alphafox
|
|
22
|
+
alphafox schema trading.traders.byId.start --format json --no-input
|
|
23
|
+
alphafox trading traders byId start --traderId <id> --body '{}' --dry-run --format json --no-input
|
|
24
|
+
alphafox trading traders byId start --traderId <id> --body '{}' --yes --format json --no-input
|
|
20
25
|
```
|
|
21
26
|
|
|
22
|
-
Without `--yes`, CLI exits `10` with `confirmation_required`.
|
|
27
|
+
Without `--yes`, CLI exits `10` with `confirmation_required`. Server still checks role/ownership.
|
|
28
|
+
|
|
29
|
+
Stop: `POST /api/v1/trading/traders/{traderId}/stop` with the same `--dry-run` then `--yes` sequence.
|
|
30
|
+
|
|
31
|
+
## Recovery
|
|
32
|
+
|
|
33
|
+
- `403`: missing scope or not owner — stop, do not retry `--yes`.
|
|
34
|
+
- `409`: trader already in the requested state — report, do not loop start/stop.
|
|
35
|
+
- Unknown HTTP after a write: do not auto-retry.
|
|
23
36
|
|
|
24
37
|
## operationIds
|
|
25
38
|
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
Vendored from alphafox-engine `npm/backtest-runner` so public CLI installs do not need GitHub Packages. Keep this copy aligned when the Engine runner changes.
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
# Vendored Engine tape runner
|
|
2
|
+
|
|
3
|
+
This directory is copied from `alphafox-engine/npm/backtest-runner` so the public CLI does not need GitHub Packages. Keep it aligned when the Engine runner changes.
|
|
4
|
+
|
|
5
|
+
# @alphafoxai/backtest-runner
|
|
6
|
+
|
|
7
|
+
Node 侧的 Engine Backtest **tape loader + scenario 组装**库。从交易所拉 closed OHLCV / funding,编成 wasm 运行时需要的 tape JSON 与列式 `ArrayBuffer`,并套上与 web 回测相同的默认 `executionModel`。
|
|
8
|
+
|
|
9
|
+
本包**不执行 wasm**,也不依赖 `@alphafoxai/backtest-wasm`(避免把约 40MB 的 wasm 拖进 CLI 的懒加载路径)。CLI 应同时依赖本包与 wasm 包:runner 负责行情,wasm 包负责 `planBacktest` / `runBacktest`。
|
|
10
|
+
|
|
11
|
+
## 安装
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
npm install @alphafoxai/backtest-runner --registry=https://npm.pkg.github.com
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
要求 Node >= 20。
|
|
18
|
+
|
|
19
|
+
## 发布
|
|
20
|
+
|
|
21
|
+
`main` push 通过常规 CI 后,仅当 `lib/`、入口 / 类型声明或 package 元数据发生变化时发布到 GitHub Packages。测试、README 或其他 Engine 文件单独变化不会触发 runner 发布。CI 使用当前 workflow run / attempt 生成唯一版本,并通过仓库 `GITHUB_TOKEN` 发布。
|
|
22
|
+
|
|
23
|
+
## 公共 API
|
|
24
|
+
|
|
25
|
+
```js
|
|
26
|
+
import {
|
|
27
|
+
DEFAULT_EXECUTION_MODEL,
|
|
28
|
+
TAPE_EXCHANGES,
|
|
29
|
+
resolveTapeExchange,
|
|
30
|
+
encodeOhlcvColumns,
|
|
31
|
+
createFileTapeCache,
|
|
32
|
+
loadTape,
|
|
33
|
+
assembleScenario,
|
|
34
|
+
} from "@alphafoxai/backtest-runner";
|
|
35
|
+
|
|
36
|
+
const { tape, buffers, coverageWarnings } = await loadTape({
|
|
37
|
+
exchangeId: "binance_perp_usdt",
|
|
38
|
+
symbols: ["BTC/USDT:USDT"],
|
|
39
|
+
timeframes: ["1m", "1h"],
|
|
40
|
+
fromMs,
|
|
41
|
+
toMs,
|
|
42
|
+
dataQualityMode: "strict",
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
const scenario = assembleScenario({
|
|
46
|
+
runId,
|
|
47
|
+
definitionId: "grid",
|
|
48
|
+
configSchemaVersion: 4,
|
|
49
|
+
config,
|
|
50
|
+
subscriptionTier: "pro",
|
|
51
|
+
initialEquity: 10_000,
|
|
52
|
+
traderName: "paper-grid",
|
|
53
|
+
tape,
|
|
54
|
+
});
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
| 导出 | 作用 |
|
|
58
|
+
|---|---|
|
|
59
|
+
| `DEFAULT_EXECUTION_MODEL` | web 硬编码值:`ohlc_path_4` / maker `0.0002` / taker `0.0005` / slippage `0.0001` |
|
|
60
|
+
| `TAPE_EXCHANGES` | Binance / OKX / Bybit / Bitget / Hyperliquid 线性合约定义 |
|
|
61
|
+
| `resolveTapeExchange(id)` | 解析 `binance_perp_usdt` 或 `binance` 等别名;未知 id **抛错** |
|
|
62
|
+
| `encodeOhlcvColumns(rows)` | 与 wasm 包相同的 float64 列式小端布局 |
|
|
63
|
+
| `createFileTapeCache(rootDir)` | 默认根目录 `~/.alphafox/cache/engine-backtest` |
|
|
64
|
+
| `loadTape(request, options?)` | 拉数 + 缓存缺口补齐 + 覆盖率校验 |
|
|
65
|
+
| `assembleScenario(input)` | 组装 `EngineBacktestScenario`,不跑 wasm |
|
|
66
|
+
|
|
67
|
+
`loadTape` 可注入 `ohlcvFetcher` / `fundingFetcher` / `marketsLoader`,单测不得打真实交易所。任一注入器出现时,本包拒绝再构造 live ccxt client。
|
|
68
|
+
|
|
69
|
+
## 缓存
|
|
70
|
+
|
|
71
|
+
- 语义对齐 web 的 closed-series IndexedDB 缓存:每个 `ccxtId + symbol + timeframe` 一条滚动区间,命中后只补缺口。
|
|
72
|
+
- Node 侧改为文件系统 JSON;max-age 30 天。
|
|
73
|
+
- `options.cache === false` / `"disable"` 关闭缓存;`options.cacheDir` 覆盖根目录。测试应使用临时目录。
|
|
74
|
+
|
|
75
|
+
## 代理
|
|
76
|
+
|
|
77
|
+
尊重 `HTTPS_PROXY` / `https_proxy` / `HTTP_PROXY` / `http_proxy`,并写入 ccxt 构造参数 `httpsProxy` / `httpProxy`。也可显式传入 `options.httpsProxy` / `options.httpProxy` / `options.agent`。未配置时不会假装“已走代理”。
|
|
78
|
+
|
|
79
|
+
## 分页 limit
|
|
80
|
+
|
|
81
|
+
与 web `tape-loader.series.ts` 对齐:Binance 1500、OKX 100、Bybit / Bitget 1000、Hyperliquid 5000。Bitget 另受约 89 天请求跨度限制;Hyperliquid 分页带 `until` 窗口。
|
|
82
|
+
|
|
83
|
+
## 数据质量
|
|
84
|
+
|
|
85
|
+
- `strict`(默认):任何缺口 / 缺数 / 非法 K 线都抛 `TapeDataUnavailableError`。
|
|
86
|
+
- `basic`:硬失败(缺市场、空序列、非法 K 线、拉数失败)仍抛错;软缺口进入 `coverageWarnings`。
|
|
87
|
+
- 禁止 mock 成功或静默降级。
|
|
88
|
+
|
|
89
|
+
## 已知限制(相对 alphafox-web)
|
|
90
|
+
|
|
91
|
+
- **不执行 wasm**,不做 `planBacktest` / `runBacktest`。
|
|
92
|
+
- **HIP-3**:构造 Hyperliquid 时与 web 一样只附加硬编码的 `xyz` builder DEX,不拉取完整 HIP-3 目录,也不移植 Next alias / 浏览器 HIP-3 市场列表。原生 HL USDC 线性合约是主路径;HIP-3 符号属于尽力而为。
|
|
93
|
+
- **缓存后端**是文件系统,不是 IndexedDB;key 版本仍是 `closed-series-v2`。
|
|
94
|
+
- **未知交易所 id 抛错**,web 的 `resolveEngineBacktestTapeExchange` 会静默回落到 Binance。
|
|
95
|
+
- **Aster / Gate** 不是 tape 数据源。
|
|
96
|
+
- Hyperliquid 不提供 `6h` K 线(与 web 相同,快速失败)。
|
|
97
|
+
- 本包不依赖 `@alphafoxai/backtest-wasm`;`encodeOhlcvColumns` 与必要类型自包含。`EngineBacktestTapeMarket.precisionMode` 按 Go tape 协议写出(wasm 包的 `index.d.ts` 目前漏了该字段)。
|
|
@@ -0,0 +1,423 @@
|
|
|
1
|
+
export const TAPE_SERIES_COLUMNS: readonly string[];
|
|
2
|
+
|
|
3
|
+
export function encodeOhlcvColumns(
|
|
4
|
+
rows: ReadonlyArray<readonly [number, number, number, number, number, number]>
|
|
5
|
+
): ArrayBuffer;
|
|
6
|
+
|
|
7
|
+
export const DEFAULT_EXECUTION_MODEL: {
|
|
8
|
+
readonly pricePath: "ohlc_path_4";
|
|
9
|
+
readonly makerFeeRate: 0.0002;
|
|
10
|
+
readonly takerFeeRate: 0.0005;
|
|
11
|
+
readonly slippageRate: 0.0001;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
export type TapeQuoteAsset = "USDT" | "USDC";
|
|
15
|
+
export type TapeCcxtId =
|
|
16
|
+
| "binanceusdm"
|
|
17
|
+
| "okx"
|
|
18
|
+
| "bybit"
|
|
19
|
+
| "bitget"
|
|
20
|
+
| "hyperliquid";
|
|
21
|
+
export type TapeExchangeId =
|
|
22
|
+
| "binance_perp_usdt"
|
|
23
|
+
| "okx_perp_usdt"
|
|
24
|
+
| "bybit_perp_usdt"
|
|
25
|
+
| "bitget_perp_usdt"
|
|
26
|
+
| "hyperliquid_perp_usdc";
|
|
27
|
+
|
|
28
|
+
export interface TapeExchangeDefinition {
|
|
29
|
+
readonly id: TapeExchangeId | string;
|
|
30
|
+
readonly label: string;
|
|
31
|
+
readonly ccxtId: TapeCcxtId | string;
|
|
32
|
+
readonly marketType: "swap";
|
|
33
|
+
readonly quoteAsset: TapeQuoteAsset;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export const TAPE_EXCHANGES: readonly TapeExchangeDefinition[];
|
|
37
|
+
export const PUBLIC_MARKET_EXCHANGE_BINANCE: "binance_perp_usdt";
|
|
38
|
+
export const PUBLIC_MARKET_EXCHANGE_OKX: "okx_perp_usdt";
|
|
39
|
+
export const PUBLIC_MARKET_EXCHANGE_BYBIT: "bybit_perp_usdt";
|
|
40
|
+
export const PUBLIC_MARKET_EXCHANGE_BITGET: "bitget_perp_usdt";
|
|
41
|
+
export const PUBLIC_MARKET_EXCHANGE_HYPERLIQUID: "hyperliquid_perp_usdc";
|
|
42
|
+
export const HYPERLIQUID_PUBLIC_MARKET_DEXES: readonly string[];
|
|
43
|
+
|
|
44
|
+
export function resolveTapeExchange(
|
|
45
|
+
exchangeId: string | TapeExchangeDefinition
|
|
46
|
+
): TapeExchangeDefinition;
|
|
47
|
+
|
|
48
|
+
export interface TapeExchangeRuntimeConfig {
|
|
49
|
+
readonly ohlcvPageLimit: number;
|
|
50
|
+
readonly fundingPageLimit: number;
|
|
51
|
+
readonly requestParams: Readonly<Record<string, unknown>>;
|
|
52
|
+
readonly constructorOptions?: Readonly<Record<string, unknown>>;
|
|
53
|
+
readonly unsupportedTimeframes?: readonly string[];
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export function tapeExchangeRuntimeConfig(
|
|
57
|
+
exchange: TapeExchangeDefinition
|
|
58
|
+
): TapeExchangeRuntimeConfig;
|
|
59
|
+
|
|
60
|
+
export const DEFAULT_TAPE_CACHE_DIR: string;
|
|
61
|
+
export const CACHE_KEY_VERSION: "closed-series-v2";
|
|
62
|
+
export const CACHE_MAX_AGE_MS: number;
|
|
63
|
+
|
|
64
|
+
export type CachedOhlcvRow = readonly [
|
|
65
|
+
number,
|
|
66
|
+
number,
|
|
67
|
+
number,
|
|
68
|
+
number,
|
|
69
|
+
number,
|
|
70
|
+
number,
|
|
71
|
+
];
|
|
72
|
+
|
|
73
|
+
export interface CachedOhlcvRange {
|
|
74
|
+
readonly sinceMs: number;
|
|
75
|
+
readonly untilMs: number;
|
|
76
|
+
readonly rows: CachedOhlcvRow[];
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export interface OhlcvRangeSegment {
|
|
80
|
+
readonly sinceMs: number;
|
|
81
|
+
readonly untilMs: number;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export interface FileTapeCache {
|
|
85
|
+
readonly disabled: boolean;
|
|
86
|
+
readonly rootDir?: string;
|
|
87
|
+
read(key: string): Promise<CachedOhlcvRange | null>;
|
|
88
|
+
write(key: string, range: CachedOhlcvRange): Promise<void>;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export function ohlcvSeriesCacheKey(
|
|
92
|
+
exchangeId: string,
|
|
93
|
+
symbol: string,
|
|
94
|
+
timeframe: string
|
|
95
|
+
): string;
|
|
96
|
+
export function missingCachedOhlcvRanges(
|
|
97
|
+
cached: CachedOhlcvRange | null,
|
|
98
|
+
sinceMs: number,
|
|
99
|
+
untilMs: number
|
|
100
|
+
): OhlcvRangeSegment[];
|
|
101
|
+
export function mergeCachedOhlcvRanges(
|
|
102
|
+
cached: CachedOhlcvRange | null,
|
|
103
|
+
addition: CachedOhlcvRange
|
|
104
|
+
): CachedOhlcvRange;
|
|
105
|
+
export function createFileTapeCache(
|
|
106
|
+
rootDir?: string,
|
|
107
|
+
options?: { maxAgeMs?: number; nowMs?: number }
|
|
108
|
+
): FileTapeCache;
|
|
109
|
+
export function createDisabledTapeCache(): FileTapeCache;
|
|
110
|
+
|
|
111
|
+
export type TapeDataQualityMode = "strict" | "basic";
|
|
112
|
+
export type TapeDataIssueCode =
|
|
113
|
+
| "market_missing"
|
|
114
|
+
| "ohlcv_missing"
|
|
115
|
+
| "invalid_ohlcv"
|
|
116
|
+
| "non_monotonic"
|
|
117
|
+
| "internal_gap"
|
|
118
|
+
| "prefix_gap"
|
|
119
|
+
| "suffix_gap"
|
|
120
|
+
| "warmup_insufficient"
|
|
121
|
+
| "load_failed"
|
|
122
|
+
| "coverage_insufficient";
|
|
123
|
+
|
|
124
|
+
export interface TapeDataIssue {
|
|
125
|
+
readonly code: TapeDataIssueCode;
|
|
126
|
+
readonly symbol: string;
|
|
127
|
+
readonly timeframe: string;
|
|
128
|
+
readonly expected?: number;
|
|
129
|
+
readonly actual?: number;
|
|
130
|
+
readonly timestamp?: number;
|
|
131
|
+
readonly message?: string;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
export class TapeDataUnavailableError extends Error {
|
|
135
|
+
readonly issues: readonly TapeDataIssue[];
|
|
136
|
+
constructor(issues: readonly TapeDataIssue[]);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
export function isTapeDataUnavailableError(
|
|
140
|
+
value: unknown
|
|
141
|
+
): value is TapeDataUnavailableError;
|
|
142
|
+
|
|
143
|
+
export type EngineBacktestPrecisionMode = "DECIMAL_PLACES" | "TICK_SIZE";
|
|
144
|
+
export type EngineBacktestSubscriptionTier = "free" | "pro" | "pro_max";
|
|
145
|
+
export type EngineBacktestPricePath = "ohlc_path_4" | "close_only";
|
|
146
|
+
|
|
147
|
+
export interface EngineBacktestExecutionModel {
|
|
148
|
+
readonly pricePath: EngineBacktestPricePath;
|
|
149
|
+
readonly makerFeeRate: number;
|
|
150
|
+
readonly takerFeeRate: number;
|
|
151
|
+
readonly slippageRate: number;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
export interface EngineBacktestTapeMarket {
|
|
155
|
+
readonly id: string;
|
|
156
|
+
readonly symbol: string;
|
|
157
|
+
readonly base?: string;
|
|
158
|
+
readonly quote?: string;
|
|
159
|
+
readonly settle?: string;
|
|
160
|
+
readonly contractSize: number;
|
|
161
|
+
readonly linear: boolean;
|
|
162
|
+
readonly precisionMode: EngineBacktestPrecisionMode;
|
|
163
|
+
readonly precisionAmount?: number;
|
|
164
|
+
readonly precisionPrice?: number;
|
|
165
|
+
readonly amountMin?: number;
|
|
166
|
+
readonly amountMax?: number;
|
|
167
|
+
readonly priceMin?: number;
|
|
168
|
+
readonly costMin?: number;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
export interface EngineBacktestTapeSeriesRef {
|
|
172
|
+
readonly symbol: string;
|
|
173
|
+
readonly timeframe: string;
|
|
174
|
+
readonly buffer: string;
|
|
175
|
+
readonly rows: number;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
export interface EngineBacktestFundingSample {
|
|
179
|
+
readonly timestamp: number;
|
|
180
|
+
readonly rate: number;
|
|
181
|
+
readonly interval: string;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
export interface EngineBacktestTapeInput {
|
|
185
|
+
readonly from: string;
|
|
186
|
+
readonly to: string;
|
|
187
|
+
readonly baseTimeframe: string;
|
|
188
|
+
readonly markets: Readonly<Record<string, EngineBacktestTapeMarket>>;
|
|
189
|
+
readonly series: readonly EngineBacktestTapeSeriesRef[];
|
|
190
|
+
readonly fundingRates?: Readonly<
|
|
191
|
+
Record<string, readonly EngineBacktestFundingSample[]>
|
|
192
|
+
>;
|
|
193
|
+
readonly lowLiquiditySymbols?: readonly string[];
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
export interface EngineBacktestScenarioTrader {
|
|
197
|
+
readonly id?: string;
|
|
198
|
+
readonly name?: string;
|
|
199
|
+
readonly strategyDefinitionId: string;
|
|
200
|
+
readonly configSchemaVersion: number;
|
|
201
|
+
readonly subscriptionTier: EngineBacktestSubscriptionTier;
|
|
202
|
+
readonly config: unknown;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
export interface EngineBacktestScenario {
|
|
206
|
+
readonly version: 1;
|
|
207
|
+
readonly runId: string;
|
|
208
|
+
readonly trader: EngineBacktestScenarioTrader;
|
|
209
|
+
readonly exchange: {
|
|
210
|
+
readonly positionSideDual: boolean;
|
|
211
|
+
readonly initialEquity: number;
|
|
212
|
+
};
|
|
213
|
+
readonly executionModel: EngineBacktestExecutionModel;
|
|
214
|
+
readonly tape: EngineBacktestTapeInput;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
export interface EngineBacktestSeriesRequirement {
|
|
218
|
+
readonly symbol: string;
|
|
219
|
+
readonly timeframe: string;
|
|
220
|
+
readonly minWarmupCandles: number;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
export interface EngineBacktestAuxiliaryDataRequirement {
|
|
224
|
+
readonly kind: string;
|
|
225
|
+
readonly parameters?: Record<string, unknown>;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
export interface TapeLoadProgress {
|
|
229
|
+
readonly stage: "markets" | "ohlcv" | "validation";
|
|
230
|
+
readonly detail: string;
|
|
231
|
+
readonly fraction: number;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
export interface TapeMarketsSnapshot {
|
|
235
|
+
readonly markets: Readonly<Record<string, TapeSymbolMarket>>;
|
|
236
|
+
readonly precisionMode?: number;
|
|
237
|
+
readonly nowMs?: number;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
export interface TapeSymbolMarket {
|
|
241
|
+
readonly id?: string;
|
|
242
|
+
readonly symbol?: string;
|
|
243
|
+
readonly created?: number;
|
|
244
|
+
readonly base?: string;
|
|
245
|
+
readonly quote?: string;
|
|
246
|
+
readonly settle?: string;
|
|
247
|
+
readonly active?: boolean;
|
|
248
|
+
readonly type?: string;
|
|
249
|
+
readonly swap?: boolean;
|
|
250
|
+
readonly linear?: boolean;
|
|
251
|
+
readonly inverse?: boolean;
|
|
252
|
+
readonly contractSize?: number;
|
|
253
|
+
readonly precision?: { readonly amount?: number; readonly price?: number };
|
|
254
|
+
readonly limits?: {
|
|
255
|
+
readonly amount?: { readonly min?: number; readonly max?: number };
|
|
256
|
+
readonly price?: { readonly min?: number };
|
|
257
|
+
readonly cost?: { readonly min?: number };
|
|
258
|
+
};
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
export type TapeOhlcvFetcher = (
|
|
262
|
+
symbol: string,
|
|
263
|
+
timeframe: string,
|
|
264
|
+
since?: number,
|
|
265
|
+
limit?: number,
|
|
266
|
+
params?: Record<string, unknown>
|
|
267
|
+
) => Promise<ReadonlyArray<readonly number[]>>;
|
|
268
|
+
|
|
269
|
+
export type TapeFundingFetcher = (
|
|
270
|
+
symbol: string,
|
|
271
|
+
since?: number,
|
|
272
|
+
limit?: number,
|
|
273
|
+
params?: Record<string, unknown>
|
|
274
|
+
) => Promise<ReadonlyArray<{ timestamp?: number; fundingRate?: number }>>;
|
|
275
|
+
|
|
276
|
+
export interface TapeLoadRequest {
|
|
277
|
+
readonly exchangeId?: string;
|
|
278
|
+
readonly exchange?: TapeExchangeDefinition;
|
|
279
|
+
readonly symbols: readonly string[];
|
|
280
|
+
readonly baseTimeframe?: string;
|
|
281
|
+
readonly timeframes: readonly string[];
|
|
282
|
+
readonly seriesRequirements?: readonly EngineBacktestSeriesRequirement[];
|
|
283
|
+
readonly needsFunding?: boolean;
|
|
284
|
+
readonly auxiliaryDataRequirements?: readonly EngineBacktestAuxiliaryDataRequirement[];
|
|
285
|
+
readonly fromMs: number;
|
|
286
|
+
readonly toMs: number;
|
|
287
|
+
readonly dataQualityMode?: TapeDataQualityMode;
|
|
288
|
+
readonly signal?: AbortSignal;
|
|
289
|
+
readonly onProgress?: (progress: TapeLoadProgress) => void;
|
|
290
|
+
readonly nowMs?: number;
|
|
291
|
+
readonly ohlcvFetcher?: TapeOhlcvFetcher;
|
|
292
|
+
readonly fundingFetcher?: TapeFundingFetcher;
|
|
293
|
+
readonly marketsLoader?: () => Promise<TapeMarketsSnapshot>;
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
export interface TapeProxyOptions {
|
|
297
|
+
readonly httpsProxy?: string;
|
|
298
|
+
readonly httpProxy?: string;
|
|
299
|
+
readonly agent?: unknown;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
export interface TapeLoadOptions extends TapeProxyOptions {
|
|
303
|
+
readonly cache?: FileTapeCache | false | "disable";
|
|
304
|
+
readonly cacheDir?: string;
|
|
305
|
+
readonly nowMs?: number;
|
|
306
|
+
readonly onProgress?: (progress: TapeLoadProgress) => void;
|
|
307
|
+
readonly createExchange?: (
|
|
308
|
+
exchange: TapeExchangeDefinition,
|
|
309
|
+
constructorOptions: Record<string, unknown>
|
|
310
|
+
) => Promise<TapeRuntimeExchange> | TapeRuntimeExchange;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
export interface TapeRuntimeExchange {
|
|
314
|
+
markets?: Readonly<Record<string, TapeSymbolMarket>>;
|
|
315
|
+
precisionMode?: number;
|
|
316
|
+
loadMarkets(): Promise<Readonly<Record<string, TapeSymbolMarket>>>;
|
|
317
|
+
fetchTime(): Promise<number>;
|
|
318
|
+
fetchOHLCV: TapeOhlcvFetcher;
|
|
319
|
+
fetchFundingRateHistory?: TapeFundingFetcher;
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
export interface TapeChartSeries {
|
|
323
|
+
readonly symbol: string;
|
|
324
|
+
readonly timeframe: string;
|
|
325
|
+
readonly rows: number;
|
|
326
|
+
readonly buffer: ArrayBuffer;
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
export interface TapeLoadResult {
|
|
330
|
+
readonly tape: EngineBacktestTapeInput;
|
|
331
|
+
readonly buffers: Record<string, ArrayBuffer>;
|
|
332
|
+
readonly coverageWarnings: readonly string[];
|
|
333
|
+
readonly chartData?: {
|
|
334
|
+
readonly exchangeId: string;
|
|
335
|
+
readonly series: readonly TapeChartSeries[];
|
|
336
|
+
};
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
export function loadTape(
|
|
340
|
+
request: TapeLoadRequest,
|
|
341
|
+
options?: TapeLoadOptions
|
|
342
|
+
): Promise<TapeLoadResult>;
|
|
343
|
+
|
|
344
|
+
export interface AssembleScenarioInput {
|
|
345
|
+
readonly runId: string;
|
|
346
|
+
readonly definitionId: string;
|
|
347
|
+
readonly configSchemaVersion: number;
|
|
348
|
+
readonly config: unknown;
|
|
349
|
+
readonly subscriptionTier: EngineBacktestSubscriptionTier;
|
|
350
|
+
readonly initialEquity: number;
|
|
351
|
+
readonly traderName?: string;
|
|
352
|
+
readonly traderId?: string;
|
|
353
|
+
readonly tape?: EngineBacktestTapeInput;
|
|
354
|
+
readonly preparedTape?: { readonly tape: EngineBacktestTapeInput };
|
|
355
|
+
readonly prepared?: { readonly tape: EngineBacktestTapeInput };
|
|
356
|
+
readonly executionModel?: Partial<EngineBacktestExecutionModel>;
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
export function assembleScenario(
|
|
360
|
+
input: AssembleScenarioInput
|
|
361
|
+
): EngineBacktestScenario;
|
|
362
|
+
|
|
363
|
+
export const TIMEFRAME_MS: Readonly<Record<string, number>>;
|
|
364
|
+
export const ENGINE_BACKTEST_BASE_TIMEFRAME: string;
|
|
365
|
+
export const ENGINE_BACKTEST_WARMUP_CANDLES: number;
|
|
366
|
+
|
|
367
|
+
export function resolvePlanBaseTimeframe(input: {
|
|
368
|
+
readonly baseTimeframe?: string | null;
|
|
369
|
+
readonly timeframes?: readonly string[] | null;
|
|
370
|
+
}): string;
|
|
371
|
+
|
|
372
|
+
export function abortable<T>(
|
|
373
|
+
operation: Promise<T>,
|
|
374
|
+
signal?: AbortSignal
|
|
375
|
+
): Promise<T>;
|
|
376
|
+
|
|
377
|
+
export function resolveCcxtProxyOptions(
|
|
378
|
+
options?: TapeProxyOptions,
|
|
379
|
+
env?: NodeJS.ProcessEnv
|
|
380
|
+
): { httpsProxy?: string; httpProxy?: string; agent?: unknown };
|
|
381
|
+
|
|
382
|
+
export function buildCcxtConstructorOptions(
|
|
383
|
+
exchange: TapeExchangeDefinition,
|
|
384
|
+
options?: TapeProxyOptions & { runtimeConfig?: TapeExchangeRuntimeConfig }
|
|
385
|
+
): Record<string, unknown>;
|
|
386
|
+
|
|
387
|
+
export function isClosedCandle(
|
|
388
|
+
timestampMs: number,
|
|
389
|
+
timeframe: string,
|
|
390
|
+
toMs: number
|
|
391
|
+
): boolean;
|
|
392
|
+
|
|
393
|
+
export function ohlcvSeriesStartMs(
|
|
394
|
+
fromMs: number,
|
|
395
|
+
timeframe: string,
|
|
396
|
+
market?: Pick<TapeSymbolMarket, "created">
|
|
397
|
+
): number;
|
|
398
|
+
|
|
399
|
+
export function effectiveTapeEndMs(
|
|
400
|
+
requestedToMs: number,
|
|
401
|
+
nowMs?: number,
|
|
402
|
+
baseTimeframe?: string
|
|
403
|
+
): number;
|
|
404
|
+
|
|
405
|
+
export function inferFundingIntervals(
|
|
406
|
+
samples: readonly Omit<EngineBacktestFundingSample, "interval">[]
|
|
407
|
+
): EngineBacktestFundingSample[];
|
|
408
|
+
|
|
409
|
+
export function classifyTapeSymbolsForPreflight(
|
|
410
|
+
symbols: readonly string[],
|
|
411
|
+
markets: Readonly<Record<string, TapeSymbolMarket>>,
|
|
412
|
+
quoteAsset: TapeQuoteAsset
|
|
413
|
+
): {
|
|
414
|
+
readonly availableSymbols: readonly string[];
|
|
415
|
+
readonly missingSymbols: readonly string[];
|
|
416
|
+
};
|
|
417
|
+
|
|
418
|
+
export function resolveEngineBacktestPrecisionMode(
|
|
419
|
+
precisionMode: number | undefined,
|
|
420
|
+
exchangeLabel: string
|
|
421
|
+
): EngineBacktestPrecisionMode;
|
|
422
|
+
|
|
423
|
+
export function resolveTapeCache(options?: TapeLoadOptions): FileTapeCache;
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
export { TAPE_SERIES_COLUMNS, encodeOhlcvColumns } from "./lib/encode.mjs";
|
|
2
|
+
export {
|
|
3
|
+
DEFAULT_EXECUTION_MODEL,
|
|
4
|
+
assembleScenario,
|
|
5
|
+
} from "./lib/scenario.mjs";
|
|
6
|
+
export {
|
|
7
|
+
TAPE_EXCHANGES,
|
|
8
|
+
PUBLIC_MARKET_EXCHANGE_BINANCE,
|
|
9
|
+
PUBLIC_MARKET_EXCHANGE_OKX,
|
|
10
|
+
PUBLIC_MARKET_EXCHANGE_BYBIT,
|
|
11
|
+
PUBLIC_MARKET_EXCHANGE_BITGET,
|
|
12
|
+
PUBLIC_MARKET_EXCHANGE_HYPERLIQUID,
|
|
13
|
+
HYPERLIQUID_PUBLIC_MARKET_DEXES,
|
|
14
|
+
resolveTapeExchange,
|
|
15
|
+
tapeExchangeRuntimeConfig,
|
|
16
|
+
} from "./lib/exchanges.mjs";
|
|
17
|
+
export {
|
|
18
|
+
DEFAULT_TAPE_CACHE_DIR,
|
|
19
|
+
CACHE_KEY_VERSION,
|
|
20
|
+
CACHE_MAX_AGE_MS,
|
|
21
|
+
ohlcvSeriesCacheKey,
|
|
22
|
+
missingCachedOhlcvRanges,
|
|
23
|
+
mergeCachedOhlcvRanges,
|
|
24
|
+
createFileTapeCache,
|
|
25
|
+
createDisabledTapeCache,
|
|
26
|
+
} from "./lib/cache.mjs";
|
|
27
|
+
export {
|
|
28
|
+
TapeDataUnavailableError,
|
|
29
|
+
isTapeDataUnavailableError,
|
|
30
|
+
evaluateOhlcvCoverage,
|
|
31
|
+
analyzeOhlcvCoverage,
|
|
32
|
+
} from "./lib/coverage.mjs";
|
|
33
|
+
export {
|
|
34
|
+
TIMEFRAME_MS,
|
|
35
|
+
ENGINE_BACKTEST_BASE_TIMEFRAME,
|
|
36
|
+
ENGINE_BACKTEST_WARMUP_CANDLES,
|
|
37
|
+
resolvePlanBaseTimeframe,
|
|
38
|
+
} from "./lib/timeframes.mjs";
|
|
39
|
+
export {
|
|
40
|
+
abortable,
|
|
41
|
+
} from "./lib/abortable.mjs";
|
|
42
|
+
export {
|
|
43
|
+
resolveCcxtProxyOptions,
|
|
44
|
+
buildCcxtConstructorOptions,
|
|
45
|
+
} from "./lib/proxy.mjs";
|
|
46
|
+
export {
|
|
47
|
+
isClosedCandle,
|
|
48
|
+
ohlcvSeriesStartMs,
|
|
49
|
+
fetchClosedOhlcvRange,
|
|
50
|
+
loadSeriesWithCache,
|
|
51
|
+
} from "./lib/series.mjs";
|
|
52
|
+
export {
|
|
53
|
+
loadTape,
|
|
54
|
+
resolveTapeCache,
|
|
55
|
+
effectiveTapeEndMs,
|
|
56
|
+
inferFundingIntervals,
|
|
57
|
+
classifyTapeSymbolsForPreflight,
|
|
58
|
+
resolveEngineBacktestPrecisionMode,
|
|
59
|
+
} from "./lib/tape-loader.mjs";
|