bybit-analysis 0.1.5

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 ADDED
@@ -0,0 +1,400 @@
1
+ # bybit-analysis
2
+
3
+ > **Repository publication rule**
4
+ >
5
+ > This repository previously contained API credentials in `.openclaude-profile.json` which were committed and pushed to GitHub. Even after local history cleanup, GitHub can retain old refs such as `refs/pull/*` that still expose those secrets.
6
+ >
7
+ > **Do not make this existing repository public.** Create a completely new public repository and clean-push only the current working tree without history.
8
+
9
+ Read-only analytics CLI for Bybit accounts. Outputs schema-stable Markdown for operators and versioned JSON for automation and agent consumption.
10
+
11
+ ## Install
12
+
13
+ ### Local development
14
+
15
+ ```bash
16
+ bun install
17
+ ```
18
+
19
+ ### Global install from npm
20
+
21
+ ```bash
22
+ npm i -g bybit-analysis
23
+ ```
24
+
25
+ This package uses Bun at runtime, so Bun must be installed on the machine where you run the CLI.
26
+
27
+ ## Run
28
+
29
+ ### Local source run
30
+
31
+ ```bash
32
+ bun run src/index.ts <command> [options]
33
+ ```
34
+
35
+ ### Installed CLI
36
+
37
+ ```bash
38
+ bybit-analysis <command> [options]
39
+ ```
40
+
41
+ When `.env` and `.bybit-profiles.json` live in a workspace directory rather than the current shell directory, pass `--project-root <path>`.
42
+
43
+ ```bash
44
+ bybit-analysis summary --project-root workspace/skills/bybit-analysis --profile main --window 30d
45
+ ```
46
+
47
+ Command-specific help:
48
+
49
+ ```bash
50
+ bun run src/index.ts <command> --help
51
+ bybit-analysis <command> --help
52
+ ```
53
+
54
+ ## Exchange Readiness Status
55
+
56
+ - Shared domain entities use extensible exchange identifiers (no hardcoded `exchange: "bybit"` contract in core types).
57
+ - Exchange-specific DTO mapping/normalization lives under `src/services/bybit/normalizers`.
58
+ - Composition root is provider-based via `src/services/composition/createServiceBundle.ts`.
59
+ - Provider contract is capability-based (`supportedMarketCategories`, `supportedSourceModes`, `botData`) and exposed in `ServiceBundle`.
60
+ - Shared request/config contracts keep provider payloads in generic `providerContext`; runtime config now carries explicit `exchangeProvider`; Bybit bot strategy IDs live under `providerContext.bybit.botStrategyIds`.
61
+ - `botService` is optional in `ServiceBundle` and only required by providers that expose bot capability.
62
+ - Current implementation status: only the `bybit` provider is implemented and registered.
63
+ - This is not full multi-exchange support yet; it is a structural split so new providers can be added without rewriting shared domain models.
64
+
65
+ ## Exit Codes (Automation Contract)
66
+
67
+ - `0` complete success (and for `health`, connectivity/auth checks passed)
68
+ - `3` optional partial success (report generated, but only optional enrichment/data degraded)
69
+ - `4` critical incomplete analytics (report generated, but output is not safely actionable for automation; expected spot-intrinsic unsupported exposure/risk reports stay successful)
70
+ - `5` health-check failure (`health` command returned failed connectivity and/or auth)
71
+ - `1` runtime failure (unexpected execution error)
72
+ - `2` usage/config failure (invalid args or runtime validation error)
73
+
74
+ Automation should branch on exit code only; no prose parsing is required.
75
+
76
+ ## Testing
77
+
78
+ ```bash
79
+ # unit + integration suite
80
+ bun run test
81
+
82
+ # watch mode during development
83
+ bun run test:watch
84
+
85
+ # optional local coverage report
86
+ bun run test:coverage
87
+
88
+ # standard local gate (types + tests + build)
89
+ bun run verify
90
+
91
+ # release validation before tag/release
92
+ bun run validate:local-release
93
+ ```
94
+
95
+ Current suite covers production-critical paths: spot PnL normalization, pagination safety handling, secret redaction, CLI stdout/stderr contract, all report schema contracts, and CLI smoke/integration flow.
96
+
97
+ ## CI / release flow
98
+
99
+ - CI workflow: `.github/workflows/verify.yml`
100
+ - npm publish workflow: `.github/workflows/npm-publish.yml`
101
+ - CI runs on pull requests, pushes to `main`, version tags `v*`, and can be started manually with `workflow_dispatch`
102
+ - npm publish is triggered by GitHub Release `published` and validates `release.tag_name == v<package.json.version>`
103
+ - npm publish requires `NPM_TOKEN` in the new public GitHub repository secrets
104
+
105
+ ### npm release steps
106
+
107
+ 1. Bump `package.json.version`.
108
+ 2. Commit and push changes to the new public repository.
109
+ 3. Create and push tag `v<version>`.
110
+ 4. Publish a GitHub Release from that tag.
111
+ 5. GitHub Actions publishes `bybit-analysis` to npm with the `latest` tag.
112
+
113
+ ## Open-source publication safety
114
+
115
+ Before publishing this project publicly:
116
+
117
+ 1. Create a new public GitHub repository.
118
+ 2. Copy or clean-push only the current working tree.
119
+ 3. Do not migrate old git history from this private repository.
120
+ 4. Run `bun run validate:local-release` before creating the release tag.
121
+ 5. Verify `npm pack --dry-run` contains only intended package files.
122
+
123
+ Do not publish local secret material such as `.env`, `.bybit-profiles.json`, `.openclaude-profile.json`, tarballs, or ad-hoc local logs.
124
+
125
+
126
+ ## Commands
127
+
128
+ - `summary` - Period analytics summary (explicitly mixes period metrics with current snapshot context)
129
+ - `balance` - Live wallet/equity/margin balances
130
+ - `pnl` - Period PnL analysis
131
+ - `positions` - Live open position inventory and status
132
+ - `exposure` - Live exposure and concentration analysis
133
+ - `performance` - Period ROI and capital efficiency analysis
134
+ - `risk` - Live leverage and downside risk analysis
135
+ - `bots` - Optional bot/copy-trading analytics
136
+ - `permissions` - Live API key permission diagnostics
137
+ - `config` - Effective runtime config (redacted)
138
+ - `health` - Live API/connectivity/readiness checks
139
+
140
+ ## Markdown Schema Contracts (All Commands)
141
+
142
+ All commands now expose schema-stable Markdown contracts:
143
+
144
+ - Report-level `Schema: <command>-markdown-v1` is present for every command.
145
+ - Report metadata lines are fixed and ordered: `Generated at`, `As Of` (for live snapshot reports when available), `Schema`, `Command`, `Outcome`, `Exit Code`, `Data Completeness`, `Health Status`, `Source Freshness`.
146
+ - Every section has a fixed `id` and is rendered as `## [section.id] Title`.
147
+ - Section order and section type are fixed per command contract.
148
+ - Sections are never removed because of missing data; reports use deterministic placeholders (`<empty>` table rows), `N/A`, `0`, `unsupported`, or info alerts.
149
+ - `Data Completeness` is a fixed section in every command contract:
150
+ - Data-backed commands render merged completeness issues in that section.
151
+ - Non data-backed commands (for example `config`, `health`, `permissions`) render explicit `unsupported` completeness status.
152
+ - `Data Completeness` states are machine-classified as `complete`, `partial_optional`, `partial_critical`, `unsupported`, or `failed`.
153
+
154
+ ## Summary Markdown Contract (`summary-markdown-v1`)
155
+
156
+ `summary` now uses a schema-stable section contract across market categories (`linear`, `spot`) and source modes (`market`, `bot`).
157
+
158
+ - Section IDs are fixed and rendered in headings as `## [section.id] Title`.
159
+ - Section order and section type are fixed.
160
+ - Section typing is pinned by explicit section contract mapping (`id + title + type`), not inferred from payload data.
161
+ - Missing category-specific data is represented as empty rows / zero values / info alerts, not by omitting sections.
162
+ - `summary.alerts` is always `alerts`; if a tabular/alternative representation is needed, it must use a different section ID and title.
163
+ - Bot enrichment failure policy:
164
+ - `--source market`: bot summary is optional enrichment. Failures do not abort report generation, but are surfaced explicitly in `summary.alerts` and `summary.data_completeness` with the original error reason.
165
+ - `--source bot`: bot summary is required input. Bot fetch failures are fail-fast and abort summary generation.
166
+
167
+ Report-level metadata:
168
+
169
+ - `Schema: summary-markdown-v1` line is present in output.
170
+
171
+ Fixed section order:
172
+
173
+ 1. `summary.contract` (`text`)
174
+ 2. `summary.overview` (`kpi`)
175
+ 3. `summary.activity` (`kpi`)
176
+ 4. `summary.allocation` (`kpi`)
177
+ 5. `summary.exposure` (`kpi`)
178
+ 6. `summary.risk` (`kpi`)
179
+ 7. `summary.open_positions` (`table`)
180
+ 8. `summary.top_holdings` (`table`)
181
+ 9. `summary.symbol_pnl` (`table`)
182
+ 10. `summary.bots` (`table`)
183
+ 11. `summary.alerts` (`alerts`)
184
+ 12. `summary.data_completeness` (`alerts`)
185
+
186
+ ## Spot PnL Inventory Method
187
+
188
+ - Cost basis method: `weighted_average`.
189
+ - Opening inventory at `--from` is reconstructed from pre-window spot executions (lookback: last 365 days) for symbols sold inside the window.
190
+ - If sell quantity cannot be matched to reconstructed inventory, the report is marked `dataCompleteness.state=partial_optional`, and unmatched quantity is excluded from realized PnL (no fallback to sell execution price).
191
+
192
+ ## PnL ROI Contract
193
+
194
+ - `pnl` uses explicit ROI status: `supported` or `unsupported`.
195
+ - ROI is `supported` only when both start and end equity are available.
196
+ - Start equity is resolved from `account.equityHistory` using the latest sample at or before `--from`.
197
+ - If start equity is unavailable, ROI KPI is rendered as `unsupported`, and the report includes an explicit reason in `ROI Status`.
198
+ - In the current Bybit account snapshot flow, historical equity is not fetched from a dedicated endpoint, so ROI/capital efficiency are explicitly marked `unsupported`.
199
+
200
+ Example (`pnl` section):
201
+
202
+ ```md
203
+ ## ROI Status
204
+ - Status: unsupported
205
+ - Reason: no equity sample found at or before period start
206
+ ```
207
+
208
+ ## Global Options
209
+
210
+ - `--project-root <path>`
211
+ - `--profile <name>`
212
+ - `--profiles-file <path>`
213
+ - `--exchange-provider <bybit>`
214
+ - `--category <linear|spot>`
215
+ - `--source <market|bot>`
216
+ - `--fgrid-bot-ids <id1,id2,...>`
217
+ - `--spot-grid-ids <id1,id2,...>`
218
+ - `--format <md|compact|json>`
219
+ - `--from <ISO8601>` period commands only: `summary`, `pnl`, `performance`, `bots`
220
+ - `--to <ISO8601>` period commands only: `summary`, `pnl`, `performance`, `bots`
221
+ - `--window <7d|30d|90d>` period commands only: `summary`, `pnl`, `performance`, `bots`
222
+ - `--timeout-ms <number>`
223
+ - `--positions-max-pages <number>`
224
+ - `--executions-max-pages-per-chunk <number>`
225
+ - `--pagination-limit-mode <error|partial>`
226
+ - `--no-env`
227
+ - `--help, -h`
228
+
229
+ ## Config & Environment Contract
230
+
231
+ Supported env vars:
232
+
233
+ - `BYBIT_API_KEY`
234
+ - `BYBIT_SECRET`
235
+ - `BYBIT_API_SECRET`
236
+ - `BYBIT_ALLOW_INSECURE_CLI_SECRETS`
237
+ - `BYBIT_DISABLE_ENV`
238
+ - `BYBIT_PROFILE`
239
+ - `BYBIT_PROFILES_FILE`
240
+ - `BYBIT_EXCHANGE_PROVIDER`
241
+ - `BYBIT_CATEGORY`
242
+ - `BYBIT_SOURCE_MODE`
243
+ - `BYBIT_FGRID_BOT_IDS`
244
+ - `BYBIT_SPOT_GRID_IDS`
245
+ - `BYBIT_FORMAT`
246
+ - `BYBIT_TIMEOUT_MS`
247
+ - `BYBIT_WINDOW`
248
+ - `BYBIT_POSITIONS_MAX_PAGES`
249
+ - `BYBIT_EXECUTIONS_MAX_PAGES_PER_CHUNK`
250
+ - `BYBIT_PAGINATION_LIMIT_MODE`
251
+ - `BYBIT_CONFIG_DIAGNOSTICS`
252
+
253
+ Precedence rules:
254
+
255
+ - General runtime fields: `CLI args -> profile (if applicable) -> env -> defaults`
256
+ - `--project-root` changes where `.env` and the default `.bybit-profiles.json` are resolved from; absolute `--profiles-file` paths are still used as-is.
257
+ - Exchange/provider selection is explicit via `--exchange-provider` / `BYBIT_EXCHANGE_PROVIDER` (currently only `bybit` is supported).
258
+ - Credentials: `profile env references -> env -> legacy CLI flags (only with BYBIT_ALLOW_INSECURE_CLI_SECRETS=1) -> defaults`
259
+ - Time range: `--from + --to -> --window -> BYBIT_WINDOW -> default 30d window`
260
+ - Live snapshot commands reject explicit historical intent from `--from`, `--to`, `--window`, and `BYBIT_WINDOW`
261
+ - Ambient env loading can be disabled with `--no-env` or `BYBIT_DISABLE_ENV=1`
262
+
263
+ Legacy hidden aliases are intentionally removed and not supported:
264
+
265
+ - `WINDOW`
266
+ - `DEFAULT_CATEGORY`
267
+ - `DEFAULT_FORMAT`
268
+ - `DEFAULT_TIMEOUT_MS`
269
+
270
+ CLI parsing conventions:
271
+
272
+ - Value options support both `--flag value` and `--flag=value`.
273
+ - `--` stops option parsing; everything after is treated as positional arguments.
274
+ - Repeated scalar options use last-value-wins semantics.
275
+ - Repeatable list options `--fgrid-bot-ids` and `--spot-grid-ids` append values in argument order.
276
+
277
+ Parser strategy:
278
+
279
+ - The project keeps a custom parser for now to preserve strict, predictable behavior and zero runtime dependencies.
280
+ - Behavior is locked with table-driven tests in `src/cli/parseArgs.test.ts`.
281
+
282
+ Output formats:
283
+
284
+ - `md` - standard Markdown layout.
285
+ - `compact` - lossless Markdown layout with tighter spacing (presentation-only; no row/text truncation).
286
+ - `json` - versioned machine-readable envelope with report metadata, outcome classification, source freshness, stable sections, and structured `data`.
287
+
288
+ ## JSON Contract
289
+
290
+ - `--format json` emits `report-json-v1`.
291
+ - JSON includes:
292
+ - `jsonSchemaVersion`
293
+ - `reportSchemaVersion`
294
+ - `command`, `title`, `generatedAt`, `asOf`
295
+ - `outcome` (status, exit code, label, completeness class, health status)
296
+ - `dataCompleteness`
297
+ - `sources`
298
+ - `sections`
299
+ - `data` with machine-usable numeric/report payloads
300
+ - `sources` entries expose provider/exchange context, `kind`, `fetchedAt`, optional `capturedAt`, optional `exchangeServerTime`, optional period window, and cache status when known.
301
+
302
+ ## Credentials (Secure Default)
303
+
304
+ Recommended production paths:
305
+
306
+ - Environment variables (`BYBIT_API_KEY` + `BYBIT_SECRET` or `BYBIT_API_SECRET`)
307
+ - Explicit env-file launch, for example `bun --env-file=.env run src/index.ts ...`
308
+ - Profile-based env references (`--profile` + `--profiles-file` with `apiKeyEnv` / `apiSecretEnv`)
309
+ - OS secret store -> export to env before launch
310
+
311
+ Example (`.env` used explicitly via `bun --env-file=.env ...`):
312
+
313
+ ```env
314
+ BYBIT_API_KEY=xxx
315
+ BYBIT_SECRET=yyy
316
+ ```
317
+
318
+ Legacy path (deprecated, insecure):
319
+
320
+ - `--api-key` and `--api-secret` are disabled by default because command-line secrets can leak via shell history, process listing, and command logging.
321
+ - Temporary bypass only: set `BYBIT_ALLOW_INSECURE_CLI_SECRETS=1`.
322
+
323
+ ## Config Priority
324
+
325
+ - General runtime fields: `CLI args -> profile (if applicable) -> env -> defaults`
326
+ - Credentials only: `profile env references -> env -> legacy CLI flags (only with BYBIT_ALLOW_INSECURE_CLI_SECRETS=1) -> defaults`
327
+ - Repo-local `.env` is not auto-loaded; the repository sets `bunfig.toml` with `env = false` for hermetic CLI/test runs.
328
+ - Use `--no-env` or `BYBIT_DISABLE_ENV=1` when you need deterministic argv-only resolution even if the parent process exported `BYBIT_*` vars.
329
+
330
+ ## Credential Profiles
331
+
332
+ Use profiles to keep non-secret runtime settings plus env variable names for each account and switch by name:
333
+
334
+ ```bash
335
+ bun run src/index.ts summary --profile subaccount-a
336
+ ```
337
+
338
+ Default profiles file is `./.bybit-profiles.json` (or set `BYBIT_PROFILES_FILE` / `--profiles-file`).
339
+
340
+ Profiles must not contain plaintext secrets. Use `apiKeyEnv` and `apiSecretEnv` to point at exported env variable names for that profile.
341
+
342
+ Example:
343
+
344
+ ```json
345
+ {
346
+ "subaccount-a": {
347
+ "apiKeyEnv": "SUBACCOUNT_A_API_KEY",
348
+ "apiSecretEnv": "SUBACCOUNT_A_API_SECRET"
349
+ },
350
+ "subaccount-b": {
351
+ "apiKeyEnv": "SUBACCOUNT_B_API_KEY",
352
+ "apiSecretEnv": "SUBACCOUNT_B_API_SECRET",
353
+ "category": "linear",
354
+ "sourceMode": "bot",
355
+ "futuresGridBotIds": ["612330315406398322"]
356
+ }
357
+ }
358
+ ```
359
+
360
+ ## Bot Mode In CLI
361
+
362
+ You can run built-in reports against grid bots by setting:
363
+
364
+ - `--source bot`
365
+ - `--category linear|spot` (optional; default `linear`)
366
+ - `--fgrid-bot-ids <id1,id2,...>` for Futures Grid bots
367
+ - `--spot-grid-ids <id1,id2,...>` for Spot Grid bots
368
+
369
+ Those bot identifiers are resolved in the Bybit adapter layer and mapped into provider request context. Provider selection is explicit via `--exchange-provider bybit` (or `BYBIT_EXCHANGE_PROVIDER=bybit`):
370
+
371
+ - `providerContext.bybit.botStrategyIds.futuresGridBotIds`
372
+ - `providerContext.bybit.botStrategyIds.spotGridBotIds`
373
+
374
+ Or set bot IDs once via env:
375
+
376
+ - `BYBIT_FGRID_BOT_IDS=<id1,id2,...>`
377
+ - `BYBIT_SPOT_GRID_IDS=<id1,id2,...>`
378
+
379
+ Pagination safety (optional):
380
+
381
+ - `BYBIT_POSITIONS_MAX_PAGES=<number>`
382
+ - `BYBIT_EXECUTIONS_MAX_PAGES_PER_CHUNK=<number>`
383
+ - `BYBIT_PAGINATION_LIMIT_MODE=<error|partial>`
384
+
385
+ Example:
386
+
387
+ ```bash
388
+ bun run src/index.ts summary \
389
+ --source bot \
390
+ --fgrid-bot-ids 612330315406398322 \
391
+ --spot-grid-ids 612340768081708828
392
+ ```
393
+
394
+ Permissions check:
395
+
396
+ ```bash
397
+ bun run src/index.ts permissions
398
+ ```
399
+
400
+ Security note: `config` and `permissions` outputs are redacted for logs/CI and do not print raw API keys, API secrets, or full IP whitelist values.
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env bun
2
+ import "../dist/index.js";