@alphafox/cli 0.3.21 → 0.3.23
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/dist/auth/refresh.js +3 -4
- package/dist/commands/run.js +2 -2
- package/dist/engine-backtest/fetch-runtime.d.ts +4 -0
- package/dist/engine-backtest/fetch-runtime.js +103 -17
- package/dist/engine-backtest/parse-args.js +10 -2
- package/dist/engine-backtest/run-command.js +4 -1
- package/dist/engine-backtest/sweep-command.js +2 -2
- package/dist/engine-backtest/types.d.ts +5 -1
- package/dist/http/client.js +3 -3
- package/dist/skills-manifest.json +45 -45
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/docs/agents/domain.md +3 -49
- package/docs/agents/issue-tracker.md +5 -41
- package/docs/agents/triage-labels.md +2 -14
- package/package.json +1 -1
- package/skills/account/SKILL.md +1 -1
- package/skills/admin/SKILL.md +1 -1
- package/skills/alphafox/SKILL.md +6 -28
- package/skills/alphafox-shared/SKILL.md +6 -6
- package/skills/auth/SKILL.md +1 -1
- package/skills/cache/SKILL.md +3 -3
- package/skills/engine-backtest/SKILL.md +3 -3
- package/skills/exchange/SKILL.md +1 -1
- package/skills/market/SKILL.md +2 -2
- package/skills/notification/SKILL.md +1 -1
- package/skills/strategy/SKILL.md +6 -4
- package/skills/trading/SKILL.md +2 -2
- package/vendor/backtest-runner/README.md +2 -0
- package/vendor/backtest-runner/index.d.ts +8 -2
- package/vendor/backtest-runner/index.mjs +5 -2
- package/vendor/backtest-runner/lib/series.mjs +5 -171
- package/vendor/backtest-runner/lib/tape-loader-concurrency.mjs +72 -0
- package/vendor/backtest-runner/lib/tape-loader-range-plan.mjs +77 -0
- package/vendor/backtest-runner/lib/tape-loader-range.mjs +222 -0
- package/vendor/backtest-runner/lib/tape-loader.mjs +13 -41
|
@@ -1,45 +1,9 @@
|
|
|
1
|
-
# Issue tracker:
|
|
1
|
+
# Issue tracker: shared Feishu Tasks
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Matt engineering issues, specs and tickets for this repo live in **AlphaFox-Issues**, not GitHub Issues. Prefix titles with `[alphafox-cli]`. GitHub hosts code and PRs; a PR URL may be linked from the task.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
On task operations, read the infra `docs/agents/issue-tracker.md` for canonical GUIDs, sections, Type options and dependency conventions. Resolve `alphafox-infra` in the workspace described in this repo's AGENTS.md (or its explicitly selected task worktree). Read the installed `lark-task` Skill for current commands; do not maintain a second CLI recipe here. If the shared file is unavailable, discover the named existing list using the Skill and verify its identity before writing; do not create a replacement list.
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
- **Read an issue**: `gh issue view <number> --comments`, filtering comments by `jq` and also fetching labels.
|
|
9
|
-
- **List issues**: `gh issue list --state open --json number,title,body,labels,comments --jq '[.[] | {number, title, body, labels: [.labels[].name], comments: [.comments[].body]}]'` with appropriate `--label` and `--state` filters.
|
|
10
|
-
- **Comment on an issue**: `gh issue comment <number> --body "..."`
|
|
11
|
-
- **Apply / remove labels**: `gh issue edit <number> --add-label "..."` / `--remove-label "..."`
|
|
12
|
-
- **Close**: `gh issue close <number> --comment "..."`
|
|
7
|
+
A linked task's acceptance is the specification. Without a linked task, use the explicit user request and record it in the PR; task creation is not a prerequisite for local work. Routine delivery comments stay within existing task authorization; new scope or tasklists need their own decision.
|
|
13
8
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
## Pull requests as a triage surface
|
|
17
|
-
|
|
18
|
-
**PRs as a request surface: no.** _(Set to `yes` if this repo treats external PRs as feature requests; `/triage` reads this flag.)_
|
|
19
|
-
|
|
20
|
-
When set to `yes`, PRs run through the same labels and states as issues, using the `gh pr` equivalents:
|
|
21
|
-
|
|
22
|
-
- **Read a PR**: `gh pr view <number> --comments` and `gh pr diff <number>` for the diff.
|
|
23
|
-
- **List external PRs for triage**: `gh pr list --state open --json number,title,body,labels,author,authorAssociation,comments` then keep only `authorAssociation` of `CONTRIBUTOR`, `FIRST_TIME_CONTRIBUTOR`, or `NONE` (drop `OWNER`/`MEMBER`/`COLLABORATOR`).
|
|
24
|
-
- **Comment / label / close**: `gh pr comment`, `gh pr edit --add-label`/`--remove-label`, `gh pr close`.
|
|
25
|
-
|
|
26
|
-
GitHub shares one number space across issues and PRs, so a bare `#42` may be either: resolve with `gh pr view 42` and fall back to `gh issue view 42`.
|
|
27
|
-
|
|
28
|
-
## When a skill says "publish to the issue tracker"
|
|
29
|
-
|
|
30
|
-
Create a GitHub issue.
|
|
31
|
-
|
|
32
|
-
## When a skill says "fetch the relevant ticket"
|
|
33
|
-
|
|
34
|
-
Run `gh issue view <number> --comments`.
|
|
35
|
-
|
|
36
|
-
## Wayfinding operations
|
|
37
|
-
|
|
38
|
-
Used by `/wayfinder`. The **map** is a single issue with **child** issues as tickets.
|
|
39
|
-
|
|
40
|
-
- **Map**: a single issue labelled `wayfinder:map`, holding the Notes / Decisions-so-far / Fog body. `gh issue create --label wayfinder:map`.
|
|
41
|
-
- **Child ticket**: an issue linked to the map as a GitHub sub-issue (`gh api` on the sub-issues endpoint). Where sub-issues aren't enabled, add the child to a task list in the map body and put `Part of #<map>` at the top of the child body. Labels: `wayfinder:<type>` (`research`/`prototype`/`grilling`/`task`). Once claimed, the ticket is assigned to the driving dev.
|
|
42
|
-
- **Blocking**: GitHub's **native issue dependencies**, the canonical, UI-visible representation. Add an edge with `gh api --method POST repos/<owner>/<repo>/issues/<child>/dependencies/blocked_by -F issue_id=<blocker-db-id>`, where `<blocker-db-id>` is the blocker's numeric **database id** (`gh api repos/<owner>/<repo>/issues/<n> --jq .id`, _not_ the `#number` or `node_id`). GitHub reports `issue_dependencies_summary.blocked_by` (open blockers only, the live gate). Where dependencies aren't available, fall back to a `Blocked by: #<n>, #<n>` line at the top of the child body. A ticket is unblocked when every blocker is closed.
|
|
43
|
-
- **Frontier query**: list the map's open children (`gh issue list --state open`, scoped to the map's sub-issues / task list), drop any with an open blocker (`issue_dependencies_summary.blocked_by > 0`, or an open issue in the `Blocked by` line) or an assignee; first in map order wins.
|
|
44
|
-
- **Claim**: `gh issue edit <n> --add-assignee @me`, the session's first write.
|
|
45
|
-
- **Resolve**: `gh issue comment <n> --body "<answer>"`, then `gh issue close <n>`, then append a context pointer (gist + link) to the map's Decisions-so-far.
|
|
9
|
+
**PRs as a request surface: no.** For a specifically requested PR triage, inspect it on GitHub and keep engineering task state in Feishu.
|
|
@@ -1,15 +1,3 @@
|
|
|
1
|
-
# Triage
|
|
1
|
+
# Triage mapping
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
| Label in mattpocock/skills | Label in our tracker | Meaning |
|
|
6
|
-
| -------------------------- | -------------------- | ---------------------------------------- |
|
|
7
|
-
| `needs-triage` | `needs-triage` | Maintainer needs to evaluate this issue |
|
|
8
|
-
| `needs-info` | `needs-info` | Waiting on reporter for more information |
|
|
9
|
-
| `ready-for-agent` | `ready-for-agent` | Fully specified, ready for an AFK agent |
|
|
10
|
-
| `ready-for-human` | `ready-for-human` | Requires human implementation |
|
|
11
|
-
| `wontfix` | `wontfix` | Will not be actioned |
|
|
12
|
-
|
|
13
|
-
When a skill mentions a role (e.g. "apply the AFK-ready triage label"), use the corresponding label string from this table.
|
|
14
|
-
|
|
15
|
-
Edit the right-hand column to match whatever vocabulary you actually use.
|
|
3
|
+
Read only when classifying a Matt engineering task. Canonical triage roles map to Feishu **sections**, not GitHub labels; categories map to the separate single-select **Type** field. The infra `docs/agents/issue-tracker.md` owns the mapping and GUIDs; resolve it through this repo's AGENTS.md. A section is not a completion status: complete/incomplete follows task acceptance. Do not rename sections or create Type options just to match a Skill's spelling.
|
package/package.json
CHANGED
package/skills/account/SKILL.md
CHANGED
package/skills/admin/SKILL.md
CHANGED
package/skills/alphafox/SKILL.md
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: alphafox
|
|
3
|
-
description:
|
|
4
|
-
version: 0.3.
|
|
3
|
+
description: "Route AlphaFox product CLI requests: installation/auth, market data, strategy configuration, backtests, traders, connectors, accounts and notifications. Repository development or AGENTS/Skills maintenance uses repository instructions, not product CLI operations."
|
|
4
|
+
version: 0.3.23
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
# AlphaFox
|
|
8
8
|
|
|
9
9
|
This skill only routes. After choosing a row, **read that skill's `SKILL.md` and follow it**. Do not improvise domain procedures from this file.
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
Read `alphafox-shared` before the first CLI invocation in this task; reuse it while unchanged (envelope, auth, risk, schema-first writes). Always `--format json --no-input`. Never `--token`.
|
|
12
12
|
|
|
13
13
|
Human-mentioned tickers go through `alphafox-market` (`alphafox resolve-symbols`) **before** they enter config, backtest, or writes. Keep the operator's asset class (美股 → `equity_perp` on `binance_perp_usdt`).
|
|
14
14
|
|
|
@@ -31,7 +31,7 @@ A **trader** is a running strategy instance (paper or live), not a person. Creat
|
|
|
31
31
|
| Notification channels | `alphafox-notification` |
|
|
32
32
|
| Admin-only operations | `alphafox-admin` |
|
|
33
33
|
|
|
34
|
-
|
|
34
|
+
Load only rows needed for the requested operation, reusing Skills already read in this task (typical: shared + market when resolving a ticker + the operation Skill).
|
|
35
35
|
|
|
36
36
|
- “帮我配/建一个网格/DCA/跟单策略” → `alphafox-strategy` (pick definition, ask knobs, validate `{common, strategy}`) **and** `alphafox-market` (resolve tickers) **and** `alphafox-trading` (create the trader, default `autoStart: true`). Hidden copy variants still create through `alphafox-trading`. After create, include the trader URL from `alphafox-shared`.
|
|
37
37
|
- “帮我回测这个配置” → `alphafox-strategy` (definition + config) **and** `alphafox-engine-backtest`. After a persisted run, include the backtest URL from `alphafox-shared`.
|
|
@@ -39,25 +39,7 @@ If several rows apply, load **all** of them (typical: `alphafox-shared` + `alpha
|
|
|
39
39
|
|
|
40
40
|
## Upgrade reminder
|
|
41
41
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
```text
|
|
45
|
-
[alphafox] update available: 0.3.15 -> 0.3.16. After the user confirms, run: alphafox update --format json --no-input,
|
|
46
|
-
```
|
|
47
|
-
|
|
48
|
-
If you see that notice (or `updateAvailable: true` from `alphafox update --check`):
|
|
49
|
-
|
|
50
|
-
1. Ask the user: **检测到新的版本,是否需要我帮你升级?**
|
|
51
|
-
2. Wait for an explicit yes. Do not upgrade on your own.
|
|
52
|
-
3. After they confirm:
|
|
53
|
-
|
|
54
|
-
```bash
|
|
55
|
-
alphafox update --format json --no-input
|
|
56
|
-
```
|
|
57
|
-
|
|
58
|
-
4. Tell the user to **restart the AI tool** so the new Skills load.
|
|
59
|
-
|
|
60
|
-
Do not install Skills from GitHub. Details and dry-run / check commands live in `alphafox-shared`.
|
|
42
|
+
An update notice does not interrupt the current task. Finish its deliverable first, then offer an upgrade if relevant. Use `alphafox-shared` for the co-versioned update procedure; an explicit update request already supplies intent. Do not independently install Skills from GitHub.
|
|
61
43
|
|
|
62
44
|
## After install
|
|
63
45
|
|
|
@@ -121,8 +103,4 @@ Ambiguous “帮我回测” → `alphafox-engine-backtest`, after resolving sym
|
|
|
121
103
|
|
|
122
104
|
`engine-backtest run|sweep` downloads closed OHLCV into the local tape cache. After a long-range or 1m backtest (or whenever the operator mentions disk / 缓存), read `alphafox-cache` and run `alphafox cache status --format json --no-input`.
|
|
123
105
|
|
|
124
|
-
If `data.tape.large` is true
|
|
125
|
-
|
|
126
|
-
1. Ask the user: **回测下载的历史数据比较大,要不要我帮你清理本地缓存?**
|
|
127
|
-
2. Wait for an explicit yes. Do not clean on your own.
|
|
128
|
-
3. Follow `alphafox-cache` (`alphafox cache clean --dry-run`, then `--yes`).
|
|
106
|
+
If `data.tape.large` is true, finish the backtest report first, then follow `alphafox-cache` for the optional cleanup offer. Reuse an explicit cleanup request; do not ask twice or clean an unrequested cache class.
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: alphafox-shared
|
|
3
3
|
description: Shared AlphaFox CLI rules for Agents — auth, profiles, envelopes, risk gates, public operationIds, and dashboard links after 回测 / 运行策略 / 排行榜.
|
|
4
|
-
version: 0.3.
|
|
4
|
+
version: 0.3.23
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
# AlphaFox shared Agent contract
|
|
@@ -39,7 +39,7 @@ The CLI checks npm at most once every 24 hours and only prints a notice on
|
|
|
39
39
|
|
|
40
40
|
**检测到新的版本,是否需要我帮你升级?**
|
|
41
41
|
|
|
42
|
-
|
|
42
|
+
Finish the current task before offering an unrelated upgrade. An explicit upgrade request or approval in this task is sufficient; otherwise wait for it. Then keep CLI and Skills co-versioned:
|
|
43
43
|
|
|
44
44
|
```bash
|
|
45
45
|
alphafox update --check --format json --no-input
|
|
@@ -82,7 +82,7 @@ Access tokens last ~10 minutes; the CLI refreshes them. After idle, run **one**
|
|
|
82
82
|
|
|
83
83
|
Local browser: `alphafox auth login --browser --format json --no-input` (loopback 127.0.0.1). If the browser cannot open, copy `authorizeUrl` from the error; do not invent a Device Flow retry unless the operator is headless.
|
|
84
84
|
|
|
85
|
-
Wrong environment
|
|
85
|
+
Wrong environment or missing permission blocks that operation; keep the requested profile and report the needed correction. Missing `--yes` is a confirmation gate: show the exact action and reuse an existing explicit approval only if it covers these parameters, otherwise ask once. Continue independent reads/local work where useful; do not switch profiles to evade a gate.
|
|
86
86
|
|
|
87
87
|
## Commands
|
|
88
88
|
|
|
@@ -99,13 +99,13 @@ Forbidden: `/backend`, `/control-plane`, `/signal-center`, internal secrets, non
|
|
|
99
99
|
|
|
100
100
|
## Writes — schema first, never invent fields
|
|
101
101
|
|
|
102
|
-
Before
|
|
102
|
+
Before composing a write body (`POST` / `PUT` / `PATCH` / `DELETE`), obtain its operation schema. In this task, reuse a schema for the same CLI/contract/catalog version, profile and operationId. Refresh on version/profile changes or schema-validation errors. “Read schema first” in domain Skills uses this same rule:
|
|
103
103
|
|
|
104
|
-
1.
|
|
104
|
+
1. On first use or invalidation, run `alphafox schema <operationId> --format json --no-input`.
|
|
105
105
|
2. Build the body **only** from `request.body` (property names, types, enums, required). Do not guess fields from memory, from another operationId, or from training data.
|
|
106
106
|
3. Small object: typed command + `--body '<json>'`.
|
|
107
107
|
4. Nested / large object: write a JSON file, then `--config @./payload.json`. Do not paste 20+ fields onto argv.
|
|
108
|
-
5. `--dry-run
|
|
108
|
+
5. Preview high-risk writes and new/changed mutation payloads with `--dry-run`. A previously verified low-risk payload shape does not require repeating discovery and dry-run on every batch item; per-request CLI validation still applies. Verify that the command actually supports dry-run.
|
|
109
109
|
|
|
110
110
|
CLI validates `--body` / `--config` against the catalog **before** HTTP. `body_schema` / `body_schema_missing` (exit `64`) means the payload is wrong — re-read `schema`, do not add extra keys to “make it work”. `--body` and `--config` cannot be combined. `--body @file` is also a file (same as `--config @file`).
|
|
111
111
|
|
package/skills/auth/SKILL.md
CHANGED
package/skills/cache/SKILL.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: alphafox-cache
|
|
3
3
|
description: Inspect and clean local Engine backtest caches (downloaded OHLCV tape and wasm runtime). Use when the user asks to 清理缓存, free disk, or after a large historical backtest.
|
|
4
|
-
version: 0.3.
|
|
4
|
+
version: 0.3.23
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
# Cache
|
|
@@ -18,11 +18,11 @@ alphafox cache status --format json --no-input
|
|
|
18
18
|
|
|
19
19
|
Read `data.tape.bytes`, `data.tape.files`, `data.tape.large`, `data.remindAfterBytes`. `large` is true when tape bytes ≥ `remindAfterBytes` (512 MiB).
|
|
20
20
|
|
|
21
|
-
|
|
21
|
+
When cleanup was not requested and `data.tape.large` is true, first deliver the backtest result, then offer:
|
|
22
22
|
|
|
23
23
|
**回测下载的历史数据比较大,要不要我帮你清理本地缓存?**
|
|
24
24
|
|
|
25
|
-
|
|
25
|
+
An explicit request to clean this cache or approval of this offer authorizes the displayed scope; do not ask twice. A size notice alone does not authorize deletion. Extra runtime/all-cache deletion needs its own scope.
|
|
26
26
|
|
|
27
27
|
## Clean
|
|
28
28
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: alphafox-engine-backtest
|
|
3
3
|
description: Local Engine WASM backtest (alphafox engine-backtest run|sweep) vs catalog experiment CRUD. After a persisted run, include https://www.alphafox.app/zh/dashboard/traders/backtest/{experimentId}.
|
|
4
|
-
version: 0.3.
|
|
4
|
+
version: 0.3.23
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
# Engine Backtest
|
|
@@ -33,7 +33,7 @@ alphafox schema engine_backtest.experiments.byId.sweeps.create --format json --n
|
|
|
33
33
|
|
|
34
34
|
The tape runner ships inside the CLI (plus `ccxt` for public-market pulls). The wasm / Node host is downloaded from the public Vercel Blob manifest (`engine-backtest/latest.json`) into `~/.cache/alphafox/engine-backtest/<hash>/` on first run.
|
|
35
35
|
|
|
36
|
-
|
|
36
|
+
For a requested backtest with supplied config, preserve and validate it against the definition; do not require the live-trader execution review. Use `alphafox-strategy` to resolve unknown fields or a requested guided configuration. Ask for unresolved required values or materially ambiguous choices. Respect the requested range, data-quality mode and persistence scope; a local-only request uses `--no-persist`. Live trader creation remains a separate requested and approved action.
|
|
37
37
|
|
|
38
38
|
Local overrides, in order:
|
|
39
39
|
|
|
@@ -98,7 +98,7 @@ Owner isolation and 7-day expiry are enforced by the server. Applying a coordina
|
|
|
98
98
|
2. `engine-backtest run` (reuse `--experiment` after the first create).
|
|
99
99
|
3. Read `data.metrics` / `data.engineVersion` / `data.runId` / `data.experimentId` / `data.experimentUrl`. After the run, also read `data.coverageNotice` (`warning` = mid-range candle gaps; `notice` = start / other soft gaps). When an Experiment id exists, include the backtest dashboard URL from `alphafox-shared` (`https://www.alphafox.app/zh/dashboard/traders/backtest/{experimentId}`) in the reply — do not stop at metrics or the raw CLI `experimentUrl`.
|
|
100
100
|
4. Adjust parameters and run again. Do not invent a token flag if persist returns 401 — `alphafox auth login`.
|
|
101
|
-
5. After a long-range or 1m run, follow `alphafox-cache`: `alphafox cache status`. If `data.tape.large` is true,
|
|
101
|
+
5. After a long-range or 1m run, follow `alphafox-cache`: `alphafox cache status`. If `data.tape.large` is true, finish the result first, then follow `alphafox-cache` for the optional cleanup offer; reuse any explicit cleanup approval.
|
|
102
102
|
|
|
103
103
|
## Safety
|
|
104
104
|
|
package/skills/exchange/SKILL.md
CHANGED
package/skills/market/SKILL.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: alphafox-market
|
|
3
3
|
description: Market data and ticker resolution for US equity perps, RWAs, and crypto on the same perp catalog. Use when the user names 美股, NVDA, AAPL, BTC, or any 标的. Keep the operator's asset class via symbolMetadata — do not rewrite NVDA into a crypto coin.
|
|
4
|
-
version: 0.3.
|
|
4
|
+
version: 0.3.23
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
# Market
|
|
@@ -28,7 +28,7 @@ Binance US stocks are **equity perps in the same** `binance_perp_usdt` catalog (
|
|
|
28
28
|
|
|
29
29
|
## Resolve tickers
|
|
30
30
|
|
|
31
|
-
|
|
31
|
+
Resolve a human-mentioned ticker before using it in config, backtest or a write. Reuse an exact/confirmed result for the same exchange, asset class and catalog within this task; resolve again when any of those inputs changes or the result is rejected. Mere discussion of a ticker does not require a market API call.
|
|
32
32
|
|
|
33
33
|
```bash
|
|
34
34
|
alphafox resolve-symbols BTC ETH --exchange binance --format json --no-input
|
package/skills/strategy/SKILL.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: alphafox-strategy
|
|
3
3
|
description: Strategy definitions — list types, read a definition's contract, and validate config. Creating a running strategy is creating a trader; use alphafox-trading for that. Local Engine backtest is alphafox-engine-backtest.
|
|
4
|
-
version: 0.3.
|
|
4
|
+
version: 0.3.23
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
# Strategy definitions
|
|
@@ -38,7 +38,9 @@ Explain the type from those fields. Missing a layer → say unknown; do not fill
|
|
|
38
38
|
|
|
39
39
|
## Configure with the human
|
|
40
40
|
|
|
41
|
-
|
|
41
|
+
Use the complete review below when preparing an actual trader creation/start, or when the user asks for a guided parameter design. It protects execution decisions and is not a gate for inspecting a definition, validating a supplied config, or a requested local backtest. For those tasks, preserve supplied values, validate applicable fields, and ask only for unresolved required inputs or a material ambiguity. Report completion of the requested operation; do not implicitly create/start a trader.
|
|
42
|
+
|
|
43
|
+
For the execution proposal, the human confirms the complete parameter set. Reuse that confirmation while the proposal and environment are unchanged. You may prepare and validate JSON to discover errors before the final execution approval.
|
|
42
44
|
|
|
43
45
|
1. Confirm the definition from `byId.get` in the operator's language (what it is, what drives it, how positions change).
|
|
44
46
|
2. Use the effective `configSchema` returned by `byId.get` as the sole parameter contract. It already composes the definition's `commonModules` with `strategyConfigSchema` and may contain definition-specific customization. Walk every applicable parameter, not only required fields or familiar knobs.
|
|
@@ -61,7 +63,7 @@ The human confirms the complete parameter set. You write JSON only after that re
|
|
|
61
63
|
A user override always wins, even when it equals neither default. Preserve explicit `false`, `0`, empty arrays, and empty strings when the schema allows them; they are not missing values.
|
|
62
64
|
|
|
63
65
|
6. When there are many parameters, present them in logical groups or numbered chunks so the review remains readable. After all groups are visible, ask the operator to reply **confirm all** / “全部确认”, or override paths/numbers. One overall confirmation is sufficient, but it must cover every displayed parameter. Apply overrides, show the affected rows again, and repeat until no required value is unresolved and the operator explicitly confirms the final proposal.
|
|
64
|
-
7.
|
|
66
|
+
7. A generic creation request is not approval of undisclosed execution settings. Obtain final approval before creating/starting a trader. Inspection, config validation and requested local backtests may proceed without this execution review; changing the approved execution parameters or environment requires renewed approval.
|
|
65
67
|
8. Write `strategy-config.json` as the trader object:
|
|
66
68
|
|
|
67
69
|
```json
|
|
@@ -94,7 +96,7 @@ alphafox trading strategy_definitions byId validate_config --definitionId <id> -
|
|
|
94
96
|
|
|
95
97
|
`body_schema` / `body_schema_missing` (exit `64`): re-read the operation schema. Server field-path errors: fix that path. Do not retry with a different envelope.
|
|
96
98
|
|
|
97
|
-
|
|
99
|
+
Validation-only work is complete with the result and any field errors. Continue to `alphafox-trading` or `alphafox-engine-backtest` only if the user requested that action; validation alone does not authorize creation/start or persisted work. Preserve the trading Skill’s execution approval. Dashboard URLs after requested actions live in `alphafox-shared`.
|
|
98
100
|
|
|
99
101
|
## operationIds
|
|
100
102
|
|
package/skills/trading/SKILL.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: alphafox-trading
|
|
3
3
|
description: Running strategies (traders) — create, list, start, and stop. A trader is a live or paper strategy instance (grid, dca, copy, …), not a person. Default Engine create uses autoStart true (创建即开始). Use autoStart false only when the user asks to create without starting. After create or start, include https://www.alphafox.app/zh/dashboard/traders/{traderId}.
|
|
4
|
-
version: 0.3.
|
|
4
|
+
version: 0.3.23
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
# Running strategies (traders)
|
|
@@ -32,7 +32,7 @@ alphafox api GET /api/v1/trading/traders --format json --no-input
|
|
|
32
32
|
|
|
33
33
|
Read `alphafox schema <operationId>` first. Body may only include documented `request.body` fields. Large / nested bodies use `--config @file`.
|
|
34
34
|
|
|
35
|
-
Before
|
|
35
|
+
Before executing create, follow the complete execution-parameter review in `alphafox-strategy`, including supplied configs. JSON preparation, validation and dry-run may precede approval. Reuse the confirmed proposal while parameters and environment are unchanged; obtain approval for any changed execution settings. User overrides win; resolve required values before create. Do not create merely because validation succeeded.
|
|
36
36
|
|
|
37
37
|
```bash
|
|
38
38
|
alphafox schema trading.traders.create --format json --no-input
|
|
@@ -80,6 +80,8 @@ const scenario = assembleScenario({
|
|
|
80
80
|
|
|
81
81
|
与 web `tape-loader.series.ts` 对齐:Binance 1500、OKX 100、Bybit / Bitget 1000、Hyperliquid 5000。Bitget 另受约 89 天请求跨度限制;Hyperliquid 分页带 `until` 窗口。
|
|
82
82
|
|
|
83
|
+
长区间会拆成最多 8 个可独立下载的时间窗口;不同 `symbol × timeframe` 与同一序列的时间窗口共用一个全局 8 请求池,避免嵌套并发放大。相邻窗口保留一根重叠 K 线,合并时按时间去重;同时间戳内容不一致会抛出 `invalid_ohlcv`,不会静默选取其中一份。
|
|
84
|
+
|
|
83
85
|
## 数据质量
|
|
84
86
|
|
|
85
87
|
- `basic`(默认):硬失败(缺市场、空序列、非法 K 线、拉数失败)仍抛错;软缺口进入 `coverageIssues` 与 `coverageWarnings`。起始缺口(`prefix_gap`)较轻,中间缺口(`internal_gap`)较重。
|
|
@@ -316,7 +316,7 @@ export interface TapeProxyOptions {
|
|
|
316
316
|
export interface TapeLoadOptions extends TapeProxyOptions {
|
|
317
317
|
readonly cache?: FileTapeCache | false | "disable";
|
|
318
318
|
readonly cacheDir?: string;
|
|
319
|
-
/**
|
|
319
|
+
/** Shared series and time-window request budget; default and hard cap are 8. */
|
|
320
320
|
readonly seriesConcurrency?: number;
|
|
321
321
|
readonly nowMs?: number;
|
|
322
322
|
readonly onProgress?: (progress: TapeLoadProgress) => void;
|
|
@@ -353,7 +353,7 @@ export interface TapeLoadResult {
|
|
|
353
353
|
};
|
|
354
354
|
}
|
|
355
355
|
|
|
356
|
-
export const DEFAULT_TAPE_SERIES_CONCURRENCY:
|
|
356
|
+
export const DEFAULT_TAPE_SERIES_CONCURRENCY: 8;
|
|
357
357
|
export const MAX_TAPE_SERIES_CONCURRENCY: 8;
|
|
358
358
|
|
|
359
359
|
export function resolveTapeSeriesConcurrency(value?: number): number;
|
|
@@ -364,6 +364,12 @@ export function mapWithConcurrency<T, R>(
|
|
|
364
364
|
worker: (item: T, index: number) => Promise<R>
|
|
365
365
|
): Promise<R[]>;
|
|
366
366
|
|
|
367
|
+
export function limitTapeOhlcvConcurrency(
|
|
368
|
+
exchange: Pick<TapeRuntimeExchange, "fetchOHLCV">,
|
|
369
|
+
concurrency: number,
|
|
370
|
+
signal?: AbortSignal
|
|
371
|
+
): Pick<TapeRuntimeExchange, "fetchOHLCV">;
|
|
372
|
+
|
|
367
373
|
export function loadTape(
|
|
368
374
|
request: TapeLoadRequest,
|
|
369
375
|
options?: TapeLoadOptions
|
|
@@ -55,10 +55,13 @@ export {
|
|
|
55
55
|
export {
|
|
56
56
|
DEFAULT_TAPE_SERIES_CONCURRENCY,
|
|
57
57
|
MAX_TAPE_SERIES_CONCURRENCY,
|
|
58
|
-
|
|
58
|
+
limitTapeOhlcvConcurrency,
|
|
59
59
|
mapWithConcurrency,
|
|
60
|
-
resolveTapeCache,
|
|
61
60
|
resolveTapeSeriesConcurrency,
|
|
61
|
+
} from "./lib/tape-loader-concurrency.mjs";
|
|
62
|
+
export {
|
|
63
|
+
loadTape,
|
|
64
|
+
resolveTapeCache,
|
|
62
65
|
effectiveTapeEndMs,
|
|
63
66
|
inferFundingIntervals,
|
|
64
67
|
classifyTapeSymbolsForPreflight,
|
|
@@ -12,19 +12,14 @@ import {
|
|
|
12
12
|
ENGINE_BACKTEST_WARMUP_CANDLES,
|
|
13
13
|
TIMEFRAME_MS,
|
|
14
14
|
} from "./timeframes.mjs";
|
|
15
|
+
import {
|
|
16
|
+
fetchClosedOhlcvRange,
|
|
17
|
+
isClosedCandle,
|
|
18
|
+
} from "./tape-loader-range.mjs";
|
|
15
19
|
|
|
16
20
|
const WARMUP_CANDLES = ENGINE_BACKTEST_WARMUP_CANDLES;
|
|
17
|
-
const BITGET_OHLCV_MAX_REQUEST_SPAN_MS = 89 * 86_400_000;
|
|
18
|
-
|
|
19
|
-
export { TIMEFRAME_MS };
|
|
20
21
|
|
|
21
|
-
export
|
|
22
|
-
const stepMs = TIMEFRAME_MS[timeframe];
|
|
23
|
-
if (!stepMs) {
|
|
24
|
-
throw new Error(`不支持的 timeframe:${timeframe}`);
|
|
25
|
-
}
|
|
26
|
-
return timestampMs + stepMs <= toMs;
|
|
27
|
-
}
|
|
22
|
+
export { fetchClosedOhlcvRange, isClosedCandle, TIMEFRAME_MS };
|
|
28
23
|
|
|
29
24
|
export function ohlcvSeriesStartMs(fromMs, timeframe, market) {
|
|
30
25
|
const stepMs = TIMEFRAME_MS[timeframe];
|
|
@@ -207,164 +202,3 @@ function concatMonotonicRows(head, tail) {
|
|
|
207
202
|
const lastHeadTimestamp = head[head.length - 1][0];
|
|
208
203
|
return [...head, ...tail.filter((row) => row[0] > lastHeadTimestamp)];
|
|
209
204
|
}
|
|
210
|
-
|
|
211
|
-
export async function fetchClosedOhlcvRange(request) {
|
|
212
|
-
const stepMs = TIMEFRAME_MS[request.timeframe];
|
|
213
|
-
if (!stepMs) {
|
|
214
|
-
throw new Error(`不支持的 timeframe:${request.timeframe}`);
|
|
215
|
-
}
|
|
216
|
-
const pageLimit = ohlcvPageLimitForTimeframe(
|
|
217
|
-
request.exchangeDefinition,
|
|
218
|
-
request.runtimeConfig,
|
|
219
|
-
request.timeframe
|
|
220
|
-
);
|
|
221
|
-
const rows = [];
|
|
222
|
-
const totalSpan = Math.max(1, request.toMs - request.sinceMs);
|
|
223
|
-
let cursor = request.sinceMs;
|
|
224
|
-
let emptyPrefixLowerMs = null;
|
|
225
|
-
let nonEmptyPrefixUpperMs = null;
|
|
226
|
-
let prefixResolved = false;
|
|
227
|
-
|
|
228
|
-
while (cursor < request.toMs) {
|
|
229
|
-
request.signal?.throwIfAborted();
|
|
230
|
-
const fetchSinceMs =
|
|
231
|
-
request.exchangeDefinition.ccxtId === "bitget"
|
|
232
|
-
? Math.max(0, cursor - 1)
|
|
233
|
-
: cursor;
|
|
234
|
-
const requestParams = {
|
|
235
|
-
...request.runtimeConfig.requestParams,
|
|
236
|
-
};
|
|
237
|
-
if (request.exchangeDefinition.ccxtId === "hyperliquid") {
|
|
238
|
-
requestParams.until = Math.min(
|
|
239
|
-
request.toMs,
|
|
240
|
-
fetchSinceMs + (pageLimit - 1) * stepMs
|
|
241
|
-
);
|
|
242
|
-
}
|
|
243
|
-
const page = await abortable(
|
|
244
|
-
request.exchange.fetchOHLCV(
|
|
245
|
-
request.symbol,
|
|
246
|
-
request.timeframe,
|
|
247
|
-
fetchSinceMs,
|
|
248
|
-
pageLimit,
|
|
249
|
-
requestParams
|
|
250
|
-
),
|
|
251
|
-
request.signal
|
|
252
|
-
);
|
|
253
|
-
request.signal?.throwIfAborted();
|
|
254
|
-
|
|
255
|
-
if (!prefixResolved && rows.length === 0 && emptyPrefixLowerMs !== null) {
|
|
256
|
-
if (page.length === 0) {
|
|
257
|
-
emptyPrefixLowerMs = cursor;
|
|
258
|
-
if (cursor >= request.fromMs) break;
|
|
259
|
-
} else {
|
|
260
|
-
nonEmptyPrefixUpperMs = cursor;
|
|
261
|
-
}
|
|
262
|
-
const upperMs = nonEmptyPrefixUpperMs ?? request.fromMs;
|
|
263
|
-
if (
|
|
264
|
-
nonEmptyPrefixUpperMs !== null &&
|
|
265
|
-
upperMs - emptyPrefixLowerMs <= stepMs
|
|
266
|
-
) {
|
|
267
|
-
cursor = upperMs;
|
|
268
|
-
prefixResolved = true;
|
|
269
|
-
continue;
|
|
270
|
-
}
|
|
271
|
-
cursor = midpointCursorMs(emptyPrefixLowerMs, upperMs, stepMs);
|
|
272
|
-
continue;
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
if (page.length === 0) {
|
|
276
|
-
if (!prefixResolved && rows.length === 0 && cursor < request.fromMs) {
|
|
277
|
-
emptyPrefixLowerMs = cursor;
|
|
278
|
-
cursor = midpointCursorMs(cursor, request.fromMs, stepMs);
|
|
279
|
-
continue;
|
|
280
|
-
}
|
|
281
|
-
break;
|
|
282
|
-
}
|
|
283
|
-
prefixResolved = true;
|
|
284
|
-
|
|
285
|
-
for (const raw of page) {
|
|
286
|
-
if (raw.length < 6) {
|
|
287
|
-
throw new TapeDataUnavailableError([
|
|
288
|
-
{
|
|
289
|
-
code: "invalid_ohlcv",
|
|
290
|
-
symbol: request.symbol,
|
|
291
|
-
timeframe: request.timeframe,
|
|
292
|
-
},
|
|
293
|
-
]);
|
|
294
|
-
}
|
|
295
|
-
const timestamp = Number(raw[0]);
|
|
296
|
-
if (!Number.isFinite(timestamp)) {
|
|
297
|
-
throw new TapeDataUnavailableError([
|
|
298
|
-
{
|
|
299
|
-
code: "invalid_ohlcv",
|
|
300
|
-
symbol: request.symbol,
|
|
301
|
-
timeframe: request.timeframe,
|
|
302
|
-
},
|
|
303
|
-
]);
|
|
304
|
-
}
|
|
305
|
-
if (!isClosedCandle(timestamp, request.timeframe, request.toMs)) {
|
|
306
|
-
continue;
|
|
307
|
-
}
|
|
308
|
-
if (rows.length > 0 && timestamp <= rows[rows.length - 1][0]) {
|
|
309
|
-
throw new TapeDataUnavailableError([
|
|
310
|
-
{
|
|
311
|
-
code: "non_monotonic",
|
|
312
|
-
symbol: request.symbol,
|
|
313
|
-
timeframe: request.timeframe,
|
|
314
|
-
expected: rows[rows.length - 1][0] + stepMs,
|
|
315
|
-
actual: timestamp,
|
|
316
|
-
timestamp,
|
|
317
|
-
},
|
|
318
|
-
]);
|
|
319
|
-
}
|
|
320
|
-
rows.push([
|
|
321
|
-
timestamp,
|
|
322
|
-
Number(raw[1]),
|
|
323
|
-
Number(raw[2]),
|
|
324
|
-
Number(raw[3]),
|
|
325
|
-
Number(raw[4]),
|
|
326
|
-
Number(raw[5]),
|
|
327
|
-
]);
|
|
328
|
-
}
|
|
329
|
-
const lastTimestamp = Number(page[page.length - 1][0]);
|
|
330
|
-
const nextCursor = lastTimestamp + stepMs;
|
|
331
|
-
if (nextCursor <= cursor) {
|
|
332
|
-
break;
|
|
333
|
-
}
|
|
334
|
-
cursor = nextCursor;
|
|
335
|
-
request.onFraction?.(Math.min(1, (cursor - request.sinceMs) / totalSpan));
|
|
336
|
-
}
|
|
337
|
-
if (rows.length === 0 && !request.allowEmpty) {
|
|
338
|
-
throw new TapeDataUnavailableError([
|
|
339
|
-
{
|
|
340
|
-
code: "ohlcv_missing",
|
|
341
|
-
symbol: request.symbol,
|
|
342
|
-
timeframe: request.timeframe,
|
|
343
|
-
},
|
|
344
|
-
]);
|
|
345
|
-
}
|
|
346
|
-
return rows;
|
|
347
|
-
}
|
|
348
|
-
|
|
349
|
-
function ohlcvPageLimitForTimeframe(exchange, runtimeConfig, timeframe) {
|
|
350
|
-
if (exchange.ccxtId !== "bitget") {
|
|
351
|
-
return runtimeConfig.ohlcvPageLimit;
|
|
352
|
-
}
|
|
353
|
-
const stepMs = TIMEFRAME_MS[timeframe];
|
|
354
|
-
return Math.max(
|
|
355
|
-
1,
|
|
356
|
-
Math.min(
|
|
357
|
-
runtimeConfig.ohlcvPageLimit,
|
|
358
|
-
Math.floor(BITGET_OHLCV_MAX_REQUEST_SPAN_MS / stepMs)
|
|
359
|
-
)
|
|
360
|
-
);
|
|
361
|
-
}
|
|
362
|
-
|
|
363
|
-
function midpointCursorMs(lowerMs, upperMs, stepMs) {
|
|
364
|
-
if (upperMs - lowerMs <= stepMs) {
|
|
365
|
-
return upperMs;
|
|
366
|
-
}
|
|
367
|
-
const midpointMs = lowerMs + Math.floor((upperMs - lowerMs) / 2);
|
|
368
|
-
const alignedMs = Math.floor(midpointMs / stepMs) * stepMs;
|
|
369
|
-
return Math.min(upperMs, Math.max(lowerMs + 1, alignedMs));
|
|
370
|
-
}
|