{
  "format": "siebly-agent-recipe/v1",
  "id": "bybit-candle-close-pipeline",
  "title": "Candle-Close Pipeline with Bybit APIs & WebSockets",
  "lastReviewed": "2026-05-12",
  "package": {
    "ecosystem": "npm",
    "name": "bybit-api",
    "docs": "https://siebly.io/sdk/bybit/javascript",
    "repository": "https://github.com/tiagosiebler/bybit-api"
  },
  "scope": {
    "runtime": "Node.js LTS",
    "language": "TypeScript",
    "product": "Bybit Spot",
    "defaultCategory": "spot",
    "defaultSymbol": "BTCUSDT",
    "defaultInterval": "1",
    "credentials": "public endpoints only"
  },
  "requiredSources": [
    "https://siebly.io/ai/historical-live-data-pipeline",
    "https://siebly.io/.well-known/recipes/historical-live-data-pipeline-core.json",
    "https://siebly.io/.well-known/conformance/historical-live-data/latest.json",
    "https://siebly.io/.well-known/conformance/historical-live-data/v1/schema.json",
    "https://siebly.io/.well-known/conformance/historical-live-data/v1/fixtures.json",
    "https://siebly.io/.well-known/conformance/historical-live-data/v1/runner.ts",
    "https://siebly.io/ai/candle-pipeline/bybit",
    "https://siebly.io/sdk/bybit/javascript",
    "https://siebly.io/.well-known/agent-skills/siebly-crypto-exchange-api/SKILL.md",
    "https://siebly.io/reference/glossary",
    "https://siebly.io/llms.txt",
    "https://siebly.io/llms-tasks.txt",
    "https://siebly.io/llms-full.txt",
    "https://siebly.io/.well-known/siebly-sdk-catalog.json",
    "https://github.com/tiagosiebler/bybit-api",
    "https://github.com/sieblyio/crypto-api-examples/tree/master/examples/Bybit"
  ],
  "sdkFactsToVerify": {
    "restClient": "RestClientV5",
    "websocketClient": "WebsocketClient",
    "restBackfill": {
      "method": "getKline",
      "exampleParams": {
        "category": "spot",
        "symbol": "BTCUSDT",
        "interval": "1",
        "limit": 200
      }
    },
    "websocketSubscription": {
      "method": "subscribeV5",
      "topic": "kline.1.BTCUSDT",
      "category": "spot"
    },
    "subscriptionAcknowledgement": {
      "event": "response",
      "expectedShape": {
        "op": "subscribe",
        "success": true,
        "req_id": "kline.1.BTCUSDT",
        "ret_msg": "subscribe"
      }
    },
    "klineCloseSignal": {
      "type": "WSKlineV5",
      "field": "confirm",
      "closedValue": true
    },
    "reconnectHooks": [
      "reconnect",
      "reconnected"
    ],
    "shutdown": "closeAll(true)"
  },
  "workflow": [
    "Create RestClientV5 and WebsocketClient without API keys.",
    "Subscribe first with subscribeV5('kline.1.BTCUSDT', 'spot').",
    "Wait for a response event with op='subscribe', success=true, and req_id matching the topic.",
    "Buffer live kline events after acknowledgement while REST backfill is running.",
    "Backfill recent candles with RestClientV5.getKline({ category: 'spot', symbol: 'BTCUSDT', interval: '1', limit: 200 }).",
    "Normalize REST tuples and WebSocket kline objects into one candle shape keyed by category, symbol, interval, and start time.",
    "Replay buffered events in deterministic order, skipping stale or duplicate records.",
    "Do not run strategy, indicator, signal generation, optional external alert, [order-intent](https://siebly.io/reference/glossary#order-intent), or account-decision workflows until [subscription acknowledgement](https://siebly.io/reference/glossary#subscription-acknowledgement), backfill, replay, and [readiness](https://siebly.io/reference/glossary#readiness-gate) are complete.",
    "Run strategy, indicator, signal generation, optional external alert, [order-intent](https://siebly.io/reference/glossary#order-intent), or account-decision workflows only when WSKlineV5.confirm === true.",
    "After reconnect, pause workflow readiness, await acknowledgement if needed, resync over REST, replay buffered events, then re-enable candle-close processing.",
    "Handle process signals and close WebSocket connections with closeAll(true) or the current documented equivalent."
  ],
  "lifecycleChain": [
    "config scope",
    "product/symbol/stream identity",
    "SDK REST/read method",
    "SDK WebSocket subscription",
    "subscription acknowledgement",
    "buffered live event",
    "historical REST/read row",
    "normalized store key",
    "dedupe or replay decision",
    "readiness state",
    "downstream side effect",
    "reconnect/resync boundary"
  ],
  "validationFixtures": [
    "candle_startup_requires_subscription_ack_backfill_replay_before_downstream_workflows",
    "open_candle_updates_store_without_downstream_signal",
    "final_candle_runs_downstream_once_after_replay",
    "duplicate_final_candle_after_reconnect_is_ignored",
    "out_of_order_candle_buffer_replays_by_candle_time_then_receive_time",
    "malformed_or_wrong_symbol_candle_is_logged_and_skipped",
    "reconnect_resync_blocks_candle_close_workflow_until_complete",
    "public_only_pipeline_has_no_private_clients_or_order_endpoints"
  ],
  "completionGate": "Do not mark the pipeline complete until three consecutive full data-lifecycle review passes produce no code, tests, fixtures, or documentation changes.",
  "stateGates": [
    {
      "state": "transport_open",
      "source": "open event",
      "requiredBeforeWorkflow": false
    },
    {
      "state": "subscription_request_sent",
      "source": "subscribeV5('kline.1.BTCUSDT', 'spot')",
      "requiredBeforeWorkflow": false
    },
    {
      "state": "subscription_acknowledged",
      "source": "response event with op='subscribe', success=true, req_id topic",
      "requiredBeforeWorkflow": true
    },
    {
      "state": "rest_backfill_complete",
      "source": "RestClientV5.getKline(...) result normalized into store",
      "requiredBeforeWorkflow": true
    },
    {
      "state": "buffered_replay_complete",
      "source": "Local replay of buffered kline events",
      "requiredBeforeWorkflow": true
    },
    {
      "state": "live_processing_enabled",
      "source": "Local readiness flag after reconciliation",
      "requiredBeforeWorkflow": true
    }
  ],
  "safety": [
    "No API keys are required.",
    "Do not add private clients, account reads, order placement, cancellation, or amendment.",
    "Do not copy private Bybit examples for public-only market-data tasks.",
    "Do not run workflow logic from a kline unless confirm is true.",
    "Reconnects must not create duplicate candles or duplicate workflow executions.",
    "Do not mark the pipeline complete until three consecutive full data-lifecycle review passes produce no code, tests, fixtures, or documentation changes."
  ],
  "promptFrameworkCompletionWorkflow": {
    "instruction": "Recursive completion workflow:\n1. Before implementation, save this exact prompt in docs/AI_PROMPT.md (or docs/SPEC.md when that is the project standard) and write docs/PLAN.md with phases, invariants, tests or fixtures, docs to update, and acceptance gates.\n2. Review docs/PLAN.md for missing workflows, unsafe assumptions, product/exchange-specific leakage, unclear state ownership, confirmation or recovery gaps, missing tests, and incomplete docs. Update docs/PLAN.md and repeat until one full review pass finds no actionable changes.\n3. Implement one plan phase at a time. After each phase, review changed code, tests, fixtures, docs, generated artifacts, and runtime workflows against docs/PLAN.md and this prompt. Fix gaps and repeat until that phase has no actionable changes before starting the next phase.\n4. After all phases, run a full-depth project review across every workflow, lifecycle, state transition, error path, and artifact. This is not a shallow summary pass. Fix every actionable gap and repeat until a full pass finds no further changes, then record the final review outcome in docs/PLAN.md.",
    "acceptanceRequirement": "docs/PLAN.md records the initial plan, plan-review iterations, phase review outcomes, final full-project review, validation commands, and any documented non-claims. No plan phase or project completion is accepted until the recursive review loop finds no actionable gaps, flaws, or incomplete workflows left to correct."
  }
}
