مهندسیJun 30, 2026·۷ دقیقه مطالعه

صفحه‌بندی درست کندل‌ها: Cursor به‌جای Offset

هر رابط کاربری نموداری بالاخره به یک مساله ثابت می‌رسد: کاربر نمودار را به چپ می‌کشد، از کندل‌های بارگذاری‌شده رد می‌شود و شما باید صفحه بعدی تاریخچه را بگیرید. روش گرفتن آن صفحه تعیین می‌کند نمودار شما پایدار است یا به‌شکل نامحسوسی خراب.

تله Offset

طراحی بدیهی این است: offset=500&limit=500&? — هر چه داری رد کن، دسته بعدی را بردار. روی داده ثابت جواب می‌دهد. روی بازار زنده نه، چون داده زیر پای Offset جابه‌جا می‌شود. بین درخواست اول و دوم شما، کندل‌های تازه بسته می‌شوند و به ابتدای سری اضافه می‌شوند. Offset دیگر به همان‌جایی اشاره نمی‌کند که لحظه‌ای قبل اشاره می‌کرد؛ صفحه دوم یا با صفحه اول هم‌پوشانی دارد یا یک کندل را جا می‌اندازد. کاربر بارهای تکراری یا حفره می‌بیند — معمولا دقیقا در مرز صفحه‌ها و معمولا غیرقابل بازتولید در گزارش باگ.

Cursor به داده لنگر می‌اندازد، نه به موقعیت

اندپوینت kline کرای‌هاب به‌جای Offset با Cursor صفحه‌بندی می‌کند. هر پاسخ در قالب خود next_cursor دارد — timestamp باز شدن قدیمی‌ترین کندلِ همان صفحه:

{
  "success": true,
  "ts": 1777804869193,
  "next_cursor": 1777651200000,
  "data": [ ... ]
}

برای صفحه قبلی، همان مقدار را به‌عنوان end_time برگردانید:

GET /rest/v1/market/kline?source=s1&symbols=btc&quote=usdt
    &slice=60min&size=500&end_time=1777651200000

چون Cursor یک timestamp است نه یک موقعیت، از رسیدن کندل‌های جدید به ابتدای سری اثر نمی‌گیرد. صفحه‌ای که انتخاب می‌کند با خود داده تعریف شده است. می‌توانید سال‌ها تاریخچه را رو به عقب پیمایش کنید در حالی که بازار رو به جلو تیک می‌خورد — و صفحه‌ها بدون درز کنار هم می‌نشینند.

قواعد سمت کلاینت

ویجت‌های خود ما چهار قاعده را رعایت می‌کنند و همین را به هر کلاینتی پیشنهاد می‌کنیم:

  1. صفحه‌ها را با کلید (pair, timeframe) نگه دارید و با هر تغییر، Cursor را ریست کنید — Cursorِ BTC/1h روی ETH/15m هیچ معنایی ندارد.
  2. وضعیت exhausted را دنبال کنید. وقتی صفحه‌ای کوتاه‌تر از اندازه درخواستی برگشت، سری را تمام‌شده علامت بزنید و دیگر درخواست نزنید.
  3. جلوی درخواست تکراری را بگیرید. رویدادهای اسکرول نمودار رگباری شلیک می‌شوند؛ اگر برای یک کلید درخواستی در راه است، رویداد جدید را رها کنید.
  4. بافر را محدود کنید. ما حداکثر ۵٬۰۰۰ کندل به‌ازای هر سری در حافظه نگه می‌داریم و قدیمی‌ترین‌ها را رها می‌کنیم. هیچ‌کس کندل ۱۲٬۰۰۰م را در تایم‌فریم یک‌دقیقه‌ای بررسی نمی‌کند؛ تایم‌فریم را بزرگ‌تر می‌کند.

بخش ظریف: حفظ دید کاربر

سخت‌ترین قسمت، گرفتن داده نیست — اتفاق بعد از آن است. اضافه کردن ۵۰۰ کندل به ابتدای سری، اندیس منطقی همه کندل‌ها را ۵۰۰ واحد جابه‌جا می‌کند و پیاده‌سازی ساده‌انگارانه باعث پرش ناگهانی دید می‌شود. راه‌حل، حسابداری است: بازه قابل‌مشاهده را قبل از prepend ثبت کنید و بعد از آن، همان بازه را با جابه‌جایی به اندازه تعداد کندل‌های اضافه‌شده دوباره اعمال کنید. کاربر هیچ چیزی حس نمی‌کند جز اینکه تاریخچه هر جا اسکرول کند وجود دارد.

صفحه‌بندی Offset برای آرشیو بلاگ خوب است. برای داده بازار، به خود داده لنگر بیندازید.