bybit-analysis
v0.1.7
Published
CLI for analyzing Bybit account data and printing schema-stable reports
Readme
bybit-analysis
Install
Local development
bun installGlobal install from npm
npm i -g bybit-analysisThis package uses Bun at runtime, so Bun must be installed on the machine where you run the CLI.
Run
Local source run
bun run src/index.ts <command> [options]Installed CLI
bybit-analysis <command> [options]When .env and .bybit-profiles.json live in a workspace directory rather than the current shell directory, pass --project-root <path>.
bybit-analysis summary --project-root workspace/skills/bybit-analysis --profile main --window 30dCommand-specific help:
bun run src/index.ts <command> --help
bybit-analysis <command> --helpExchange Readiness Status
- Shared domain entities use extensible exchange identifiers (no hardcoded
exchange: "bybit"contract in core types). - Exchange-specific DTO mapping/normalization lives under
src/services/bybit/normalizers. - Composition root is provider-based via
src/services/composition/createServiceBundle.ts. - Provider contract is capability-based (
supportedMarketCategories,supportedSourceModes,botData) and exposed inServiceBundle. - Shared request/config contracts keep provider payloads in generic
providerContext; runtime config now carries explicitexchangeProvider; Bybit bot strategy IDs live underproviderContext.bybit.botStrategyIds. botServiceis optional inServiceBundleand only required by providers that expose bot capability.- Current implementation status: only the
bybitprovider is implemented and registered. - This is not full multi-exchange support yet; it is a structural split so new providers can be added without rewriting shared domain models.
Exit Codes (Automation Contract)
0complete success (and forhealth, connectivity/auth checks passed)3optional partial success (report generated, but only optional enrichment/data degraded)4critical incomplete analytics (report generated, but output is not safely actionable for automation; expected spot-intrinsic unsupported exposure/risk reports stay successful)5health-check failure (healthcommand returned failed connectivity and/or auth)1runtime failure (unexpected execution error)2usage/config failure (invalid args or runtime validation error)
Automation should branch on exit code only; no prose parsing is required.
Testing
# unit + integration suite
bun run test
# watch mode during development
bun run test:watch
# optional local coverage report
bun run test:coverage
# standard local gate (types + tests + build)
bun run verify
# release validation before tag/release
bun run validate:local-releaseCurrent 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.
CI / release flow
- CI workflow:
.github/workflows/verify.yml - npm publish workflow:
.github/workflows/npm-publish.yml - CI runs on pull requests, pushes to
main, version tagsv*, and can be started manually withworkflow_dispatch - npm publish is triggered by GitHub Release
publishedand validatesrelease.tag_name == v<package.json.version> - npm publish requires
NPM_TOKENin the new public GitHub repository secrets
npm release steps
- Bump
package.json.version. - Commit and push changes to the new public repository.
- Create and push tag
v<version>. - Publish a GitHub Release from that tag.
- GitHub Actions publishes
bybit-analysisto npm with thelatesttag.
Open-source publication safety
Before publishing this project publicly:
- Create a new public GitHub repository.
- Copy or clean-push only the current working tree.
- Do not migrate old git history from this private repository.
- Run
bun run validate:local-releasebefore creating the release tag. - Verify
npm pack --dry-runcontains only intended package files.
Do not publish local secret material such as .env, .bybit-profiles.json, .openclaude-profile.json, tarballs, or ad-hoc local logs.
Commands
summary- Period analytics summary (explicitly mixes period metrics with current snapshot context)balance- Live wallet/equity/margin balancespnl- Period PnL analysispositions- Live open position inventory and statusexposure- Live exposure and concentration analysisperformance- Period ROI and capital efficiency analysisrisk- Live leverage and downside risk analysisbots- Optional bot/copy-trading analyticspermissions- Live API key permission diagnosticsconfig- Effective runtime config (redacted)health- Live API/connectivity/readiness checks
Markdown Schema Contracts (All Commands)
All commands now expose schema-stable Markdown contracts:
- Report-level
Schema: <command>-markdown-v1is present for every command. - 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. - Every section has a fixed
idand is rendered as## [section.id] Title. - Section order and section type are fixed per command contract.
- Sections are never removed because of missing data; reports use deterministic placeholders (
<empty>table rows),N/A,0,unsupported, or info alerts. Data Completenessis a fixed section in every command contract:- Data-backed commands render merged completeness issues in that section.
- Non data-backed commands (for example
config,health,permissions) render explicitunsupportedcompleteness status.
Data Completenessstates are machine-classified ascomplete,partial_optional,partial_critical,unsupported, orfailed.
Summary Markdown Contract (summary-markdown-v1)
summary now uses a schema-stable section contract across market categories (linear, spot) and source modes (market, bot).
- Section IDs are fixed and rendered in headings as
## [section.id] Title. - Section order and section type are fixed.
- Section typing is pinned by explicit section contract mapping (
id + title + type), not inferred from payload data. - Missing category-specific data is represented as empty rows / zero values / info alerts, not by omitting sections.
summary.alertsis alwaysalerts; if a tabular/alternative representation is needed, it must use a different section ID and title.- Bot enrichment failure policy:
--source market: bot summary is optional enrichment. Failures do not abort report generation, but are surfaced explicitly insummary.alertsandsummary.data_completenesswith the original error reason.--source bot: bot summary is required input. Bot fetch failures are fail-fast and abort summary generation.
Report-level metadata:
Schema: summary-markdown-v1line is present in output.
Fixed section order:
summary.contract(text)summary.overview(kpi)summary.activity(kpi)summary.allocation(kpi)summary.exposure(kpi)summary.risk(kpi)summary.open_positions(table)summary.top_holdings(table)summary.symbol_pnl(table)summary.bots(table)summary.alerts(alerts)summary.data_completeness(alerts)
Spot PnL Inventory Method
- Cost basis method:
weighted_average. - Opening inventory at
--fromis reconstructed from pre-window spot executions (lookback: last 365 days) for symbols sold inside the window. - 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).
PnL ROI Contract
pnluses explicit ROI status:supportedorunsupported.- ROI is
supportedonly when both start and end equity are available. - Start equity is resolved from
account.equityHistoryusing the latest sample at or before--from. - If start equity is unavailable, ROI KPI is rendered as
unsupported, and the report includes an explicit reason inROI Status. - In the current Bybit account snapshot flow, historical equity is not fetched from a dedicated endpoint, so ROI/capital efficiency are explicitly marked
unsupported.
Example (pnl section):
## ROI Status
- Status: unsupported
- Reason: no equity sample found at or before period startGlobal Options
--project-root <path>--profile <name>--profiles-file <path>--exchange-provider <bybit>--category <linear|spot>--source <market|bot>--fgrid-bot-ids <id1,id2,...>--spot-grid-ids <id1,id2,...>--format <md|compact|json>--from <ISO8601>period commands only:summary,pnl,performance,bots--to <ISO8601>period commands only:summary,pnl,performance,bots--window <7d|30d|90d>period commands only:summary,pnl,performance,bots--timeout-ms <number>--positions-max-pages <number>--executions-max-pages-per-chunk <number>--pagination-limit-mode <error|partial>--no-env--help, -h
Config & Environment Contract
Supported env vars:
BYBIT_API_KEYBYBIT_SECRETBYBIT_API_SECRETBYBIT_ALLOW_INSECURE_CLI_SECRETSBYBIT_DISABLE_ENVBYBIT_PROFILEBYBIT_PROFILES_FILEBYBIT_EXCHANGE_PROVIDERBYBIT_CATEGORYBYBIT_SOURCE_MODEBYBIT_FGRID_BOT_IDSBYBIT_SPOT_GRID_IDSBYBIT_FORMATBYBIT_TIMEOUT_MSBYBIT_WINDOWBYBIT_POSITIONS_MAX_PAGESBYBIT_EXECUTIONS_MAX_PAGES_PER_CHUNKBYBIT_PAGINATION_LIMIT_MODEBYBIT_CONFIG_DIAGNOSTICS
Precedence rules:
- General runtime fields:
CLI args -> profile (if applicable) -> env -> defaults --project-rootchanges where.envand the default.bybit-profiles.jsonare resolved from; absolute--profiles-filepaths are still used as-is.- Exchange/provider selection is explicit via
--exchange-provider/BYBIT_EXCHANGE_PROVIDER(currently onlybybitis supported). - Credentials:
profile env references -> env -> legacy CLI flags (only with BYBIT_ALLOW_INSECURE_CLI_SECRETS=1) -> defaults - Time range:
--from + --to -> --window -> BYBIT_WINDOW -> default 30d window - Live snapshot commands reject explicit historical intent from
--from,--to,--window, andBYBIT_WINDOW - Ambient env loading can be disabled with
--no-envorBYBIT_DISABLE_ENV=1
Legacy hidden aliases are intentionally removed and not supported:
WINDOWDEFAULT_CATEGORYDEFAULT_FORMATDEFAULT_TIMEOUT_MS
CLI parsing conventions:
- Value options support both
--flag valueand--flag=value. --stops option parsing; everything after is treated as positional arguments.- Repeated scalar options use last-value-wins semantics.
- Repeatable list options
--fgrid-bot-idsand--spot-grid-idsappend values in argument order.
Parser strategy:
- The project keeps a custom parser for now to preserve strict, predictable behavior and zero runtime dependencies.
- Behavior is locked with table-driven tests in
src/cli/parseArgs.test.ts.
Output formats:
md- standard Markdown layout.compact- lossless Markdown layout with tighter spacing (presentation-only; no row/text truncation).json- versioned machine-readable envelope with report metadata, outcome classification, source freshness, stable sections, and structureddata.
JSON Contract
--format jsonemitsreport-json-v1.- JSON includes:
jsonSchemaVersionreportSchemaVersioncommand,title,generatedAt,asOfoutcome(status, exit code, label, completeness class, health status)dataCompletenesssourcessectionsdatawith machine-usable numeric/report payloads
sourcesentries expose provider/exchange context,kind,fetchedAt, optionalcapturedAt, optionalexchangeServerTime, optional period window, and cache status when known.
Credentials (Secure Default)
Recommended production paths:
- Environment variables (
BYBIT_API_KEY+BYBIT_SECRETorBYBIT_API_SECRET) - Explicit env-file launch, for example
bun --env-file=.env run src/index.ts ... - Profile-based env references (
--profile+--profiles-filewithapiKeyEnv/apiSecretEnv) - OS secret store -> export to env before launch
Example (.env used explicitly via bun --env-file=.env ...):
BYBIT_API_KEY=xxx
BYBIT_SECRET=yyyLegacy path (deprecated, insecure):
--api-keyand--api-secretare disabled by default because command-line secrets can leak via shell history, process listing, and command logging.- Temporary bypass only: set
BYBIT_ALLOW_INSECURE_CLI_SECRETS=1.
Config Priority
- General runtime fields:
CLI args -> profile (if applicable) -> env -> defaults - Credentials only:
profile env references -> env -> legacy CLI flags (only with BYBIT_ALLOW_INSECURE_CLI_SECRETS=1) -> defaults - Repo-local
.envis not auto-loaded; the repository setsbunfig.tomlwithenv = falsefor hermetic CLI/test runs. - Use
--no-envorBYBIT_DISABLE_ENV=1when you need deterministic argv-only resolution even if the parent process exportedBYBIT_*vars.
Credential Profiles
Use profiles to keep non-secret runtime settings plus env variable names for each account and switch by name:
bun run src/index.ts summary --profile subaccount-aDefault profiles file is ./.bybit-profiles.json (or set BYBIT_PROFILES_FILE / --profiles-file).
Profiles must not contain plaintext secrets. Use apiKeyEnv and apiSecretEnv to point at exported env variable names for that profile.
Example:
{
"subaccount-a": {
"apiKeyEnv": "SUBACCOUNT_A_API_KEY",
"apiSecretEnv": "SUBACCOUNT_A_API_SECRET"
},
"subaccount-b": {
"apiKeyEnv": "SUBACCOUNT_B_API_KEY",
"apiSecretEnv": "SUBACCOUNT_B_API_SECRET",
"category": "linear",
"sourceMode": "bot",
"futuresGridBotIds": ["612330315406398322"]
}
}Bot Mode In CLI
You can run built-in reports against grid bots by setting:
--source bot--category linear|spot(optional; defaultlinear)--fgrid-bot-ids <id1,id2,...>for Futures Grid bots--spot-grid-ids <id1,id2,...>for Spot Grid bots
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):
providerContext.bybit.botStrategyIds.futuresGridBotIdsproviderContext.bybit.botStrategyIds.spotGridBotIds
Or set bot IDs once via env:
BYBIT_FGRID_BOT_IDS=<id1,id2,...>BYBIT_SPOT_GRID_IDS=<id1,id2,...>
Pagination safety (optional):
BYBIT_POSITIONS_MAX_PAGES=<number>BYBIT_EXECUTIONS_MAX_PAGES_PER_CHUNK=<number>BYBIT_PAGINATION_LIMIT_MODE=<error|partial>
Example:
bun run src/index.ts summary \
--source bot \
--fgrid-bot-ids 612330315406398322 \
--spot-grid-ids 612340768081708828Permissions check:
bun run src/index.ts permissionsSecurity note: config and permissions outputs are redacted for logs/CI and do not print raw API keys, API secrets, or full IP whitelist values.
