{
  "openapi": "3.0.3",
  "info": {
    "title": "زروان فاند — API بازار بورس تهران",
    "description": "داده‌های لحظه‌ای بازار سهام تهران (TSETMC): نمای کلی بازار، پهنای بازار، برترین‌ها، جریان پول حقیقی/حقوقی، دفتر سفارش، معاملات و فیلتر نمادها. این API فقط-خواندنی (GET/POST) و خروجی JSON است.",
    "version": "1.0.0",
    "contact": {
      "name": "زروان فاند",
      "url": "https://zarvanfund.ir"
    }
  },
  "servers": [
    {
      "url": "https://zarvanfund.ir",
      "description": "پروداکشن"
    }
  ],
  "tags": [
    {
      "name": "بازار",
      "description": "نمای کلی، پهنا و شاخص‌ها"
    },
    {
      "name": "نمادها",
      "description": "جست‌وجو، جزئیات، کندل و معاملات نماد"
    },
    {
      "name": "جریان پول",
      "description": "پول حقیقی/حقوقی و قدرت خرید"
    },
    {
      "name": "ابزار",
      "description": "فیلتر و تصویر لحظه‌ای"
    },
    {
      "name": "نشانگرها",
      "description": "موتورِ نشانگرهای typed (پیش‌محاسبه، ماندگار)"
    },
    {
      "name": "موج",
      "description": "سناریوهای شمارشِ موجِ الیوت/نئوویو (فرضیه، نه پیش‌بینی)"
    },
    {
      "name": "inventory",
      "description": "Auto-generated route inventory placeholders pending full endpoint schemas"
    }
  ],
  "paths": {
    "/api/admin/alerts": {
      "get": {
        "tags": [
          "مدیریت"
        ],
        "summary": "فهرست هشدارهای قیمتیِ سراسری",
        "description": "همهٔ هشدارهای قیمتیِ تعریف‌شده در سطح سامانه را برمی‌گرداند.",
        "responses": {
          "200": {
            "description": "پاسخِ JSON"
          },
          "401": {
            "description": "نیازمندِ احراز هویت"
          },
          "403": {
            "description": "دسترسیِ مدیریتی لازم است"
          }
        }
      }
    },
    "/api/admin/alerts/add": {
      "post": {
        "tags": [
          "مدیریت"
        ],
        "summary": "افزودن هشدار قیمتی یا رویداديِ کدال",
        "description": "هشدار جدید با نماد، عملگر مقایسه (>,<,>=,<=)، آستانه و کانالِ اعلان (telegram/bale/push) ثبت می‌کند. `metric` علاوه بر price/change_pct/volume، چهار مقدارِ رویداديِ کدال هم می‌پذیرد: `codal_eps` (تغییرِ پیش‌بینیِ EPS)، `codal_capital` (افزایشِ سرمایه)، `codal_halt` (توقف/بازگشاییِ نماد) و `codal_due` (نزدیکِ موعدِ برآوردیِ گزارشِ فصلیِ بعدی). برای این چهار مقدار op/price نادیده گرفته می‌شوند — اتصالِ خودکارِ اطلاعیهٔ کدال شرطِ عددی ندارد، فقط تطبیقِ نوعِ رویداد.",
        "responses": {
          "200": {
            "description": "پاسخِ JSON"
          },
          "401": {
            "description": "نیازمندِ احراز هویت"
          },
          "403": {
            "description": "دسترسیِ مدیریتی لازم است"
          }
        }
      }
    },
    "/api/admin/alerts/delete": {
      "post": {
        "tags": [
          "مدیریت"
        ],
        "summary": "حذف هشدار قیمتی",
        "description": "هشدارِ قیمتی را با شناسه حذف می‌کند.",
        "responses": {
          "200": {
            "description": "پاسخِ JSON"
          },
          "401": {
            "description": "نیازمندِ احراز هویت"
          },
          "403": {
            "description": "دسترسیِ مدیریتی لازم است"
          }
        }
      }
    },
    "/api/admin/audit": {
      "get": {
        "tags": [
          "مدیریت"
        ],
        "summary": "گزارشِ ممیزیِ مدیریتی",
        "description": "رویدادهای ممیزیِ مدیریتی را با فیلترِ کاربر و صفحه‌بندی فهرست می‌کند.",
        "responses": {
          "200": {
            "description": "پاسخِ JSON"
          },
          "401": {
            "description": "نیازمندِ احراز هویت"
          },
          "403": {
            "description": "دسترسیِ مدیریتی لازم است"
          }
        },
        "parameters": [
          {
            "name": "user",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "شناسهٔ کاربر"
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "حداکثر تعداد"
          },
          {
            "name": "offset",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "آفست صفحه‌بندی"
          }
        ]
      }
    },
    "/api/admin/backtest-ops": {
      "post": {
        "tags": [
          "مدیریت"
        ],
        "summary": "عملیات مدیریتیِ صفِ بک‌تست",
        "description": "اقدامِ مدیریتی (لغو/حذف/…) روی کارِ بک‌تست بر پایهٔ action/kind/id.",
        "responses": {
          "200": {
            "description": "پاسخِ JSON"
          },
          "401": {
            "description": "نیازمندِ احراز هویت"
          },
          "403": {
            "description": "دسترسیِ مدیریتی لازم است"
          }
        }
      }
    },
    "/api/admin/broadcast": {
      "post": {
        "tags": [
          "مدیریت"
        ],
        "summary": "ارسالِ پیامِ همگانی",
        "description": "پیام/اعلانِ همگانی برای کاربران ارسال می‌کند.",
        "responses": {
          "200": {
            "description": "پاسخِ JSON"
          },
          "401": {
            "description": "نیازمندِ احراز هویت"
          },
          "403": {
            "description": "دسترسیِ مدیریتی لازم است"
          }
        }
      }
    },
    "/api/admin/client-errors": {
      "get": {
        "tags": [
          "مدیریت"
        ],
        "summary": "نمای تجمیعیِ خطاهای فرانت‌اند",
        "description": "گروه‌های خطای گزارش‌شده از مرورگر را از پرتکرارترین به کم‌تکرارترین برمی‌گرداند.",
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "default": 50,
              "maximum": 200
            },
            "description": "بیشینهٔ گروه‌های بازگشتی"
          }
        ],
        "responses": {
          "200": {
            "description": "پاسخِ JSON"
          },
          "401": {
            "description": "نیازمندِ احراز هویت"
          },
          "403": {
            "description": "دسترسیِ مدیریتی لازم است"
          }
        }
      }
    },
    "/api/admin/collector": {
      "get": {
        "tags": [
          "مدیریت"
        ],
        "summary": "سلامت و آمارِ کالکتور را از /health و /stats آن می‌خواند و برای نمایش در پنل تجمیع می‌کند",
        "description": "سلامت و آمارِ کالکتور را از /health و /stats آن می‌خواند و برای نمایش در پنل تجمیع می‌کند. اگر کالکتور در دسترس نباشد، reachable=false برمی‌گردد.",
        "responses": {
          "200": {
            "description": "پاسخِ JSON"
          },
          "401": {
            "description": "نیازمندِ احراز هویت"
          },
          "403": {
            "description": "دسترسیِ مدیریتی لازم است"
          }
        }
      }
    },
    "/api/admin/collector/restart": {
      "post": {
        "tags": [
          "مدیریت"
        ],
        "summary": "درخواستِ ری‌استارت را به endpointِ کنترلِ کالکتور می‌فرستد",
        "description": "درخواستِ ری‌استارت را به endpointِ کنترلِ کالکتور می‌فرستد. نیازمندِ تنظیمِ TSETMC_CONTROL_TOKEN در هر دو سرویس است؛ در غیرِ این صورت کالکتور ۴۰۳ می‌دهد و این‌جا خطا برمی‌گردد.",
        "responses": {
          "200": {
            "description": "پاسخِ JSON"
          },
          "401": {
            "description": "نیازمندِ احراز هویت"
          },
          "403": {
            "description": "دسترسیِ مدیریتی لازم است"
          }
        }
      }
    },
    "/api/admin/data-quality": {
      "get": {
        "tags": [
          "مدیریت"
        ],
        "summary": "گزارشِ عمیقِ کیفیتِ ticks/orderbook (§14",
        "description": "گزارشِ عمیقِ کیفیتِ ticks/orderbook (§14.3) را می‌دهد. با timeoutِ اختصاصیِ ۳۰s تا کوئریِ سردِ نشست کامل شود (بدونِ وابستگی به deadlineِ کوتاهِ درخواست).",
        "responses": {
          "200": {
            "description": "پاسخِ JSON"
          },
          "401": {
            "description": "نیازمندِ احراز هویت"
          },
          "403": {
            "description": "دسترسیِ مدیریتی لازم است"
          }
        }
      }
    },
    "/api/admin/incident": {
      "post": {
        "tags": [
          "مدیریت"
        ],
        "summary": "ثبتِ رخدادِ کیفیتِ داده",
        "description": "رخدادِ داده را با نوع/منبع/شدت/دلیل ثبت و ثبتِ ممیزی می‌کند.",
        "responses": {
          "200": {
            "description": "پاسخِ JSON"
          },
          "401": {
            "description": "نیازمندِ احراز هویت"
          },
          "403": {
            "description": "دسترسیِ مدیریتی لازم است"
          }
        }
      }
    },
    "/api/admin/kill-switch": {
      "post": {
        "tags": [
          "مدیریت"
        ],
        "summary": "کلیدِ توقفِ اضطراریِ سراسری",
        "description": "فعال/غیرفعال‌کردنِ توقفِ اضطراری در دامنهٔ مشخص (scope/target_id/on/reason).",
        "responses": {
          "200": {
            "description": "پاسخِ JSON"
          },
          "401": {
            "description": "نیازمندِ احراز هویت"
          },
          "403": {
            "description": "دسترسیِ مدیریتی لازم است"
          }
        }
      }
    },
    "/api/admin/login": {
      "post": {
        "tags": [
          "مدیریت"
        ],
        "summary": "ورود ادمین: در صورت صحت، JWT برمی‌گرداند",
        "description": "ورود ادمین: در صورت صحت، JWT برمی‌گرداند.",
        "responses": {
          "200": {
            "description": "پاسخِ JSON"
          }
        }
      }
    },
    "/api/admin/logs": {
      "get": {
        "tags": [
          "مدیریت"
        ],
        "summary": "تازه‌ترین رکوردهای لاگِ نگه‌داری‌شده در حافظه را برای مانیتورِ ادمین برمی‌گرداند",
        "description": "تازه‌ترین رکوردهای لاگِ نگه‌داری‌شده در حافظه را برای مانیتورِ ادمین برمی‌گرداند. پارامترها: level (DEBUG|INFO|WARN|ERROR، پیش‌فرض INFO) و limit (≤۴۰۰).",
        "responses": {
          "200": {
            "description": "پاسخِ JSON"
          },
          "401": {
            "description": "نیازمندِ احراز هویت"
          },
          "403": {
            "description": "دسترسیِ مدیریتی لازم است"
          }
        }
      }
    },
    "/api/admin/model-validation": {
      "get": {
        "tags": [
          "مدیریت"
        ],
        "summary": "گزارش اعتبارسنجیِ مدل‌ها",
        "description": "گزارشِ اعتبارسنجیِ مدل‌های سیگنال/بک‌تست را با timeoutِ اختصاصیِ ۱۵ثانیه برمی‌گرداند.",
        "responses": {
          "200": {
            "description": "پاسخِ JSON"
          },
          "401": {
            "description": "نیازمندِ احراز هویت"
          },
          "403": {
            "description": "دسترسیِ مدیریتی لازم است"
          }
        }
      }
    },
    "/api/admin/operations": {
      "get": {
        "tags": [
          "مدیریت"
        ],
        "summary": "نمای کلیِ عملیاتِ سامانه",
        "description": "نمای وضعیتِ عملیاتیِ سامانه (جمع‌بندیِ سلامت/صف‌ها) را می‌دهد.",
        "responses": {
          "200": {
            "description": "پاسخِ JSON"
          },
          "401": {
            "description": "نیازمندِ احراز هویت"
          },
          "403": {
            "description": "دسترسیِ مدیریتی لازم است"
          }
        }
      }
    },
    "/api/admin/passkey/list": {
      "get": {
        "tags": [
          "مدیریت"
        ],
        "summary": "فهرست خلاصه‌ی passkeyهای ثبت‌شده (تعداد) را برمی‌گرداند",
        "description": "فهرست خلاصه‌ی passkeyهای ثبت‌شده (تعداد) را برمی‌گرداند.",
        "responses": {
          "200": {
            "description": "پاسخِ JSON"
          },
          "401": {
            "description": "نیازمندِ احراز هویت"
          },
          "403": {
            "description": "دسترسیِ مدیریتی لازم است"
          }
        }
      }
    },
    "/api/admin/passkey/login/begin": {
      "post": {
        "tags": [
          "مدیریت"
        ],
        "summary": "آغازِ ورود با Passkey",
        "description": "چالشِ WebAuthn برای ورودِ کلیدِ عبور را می‌سازد.",
        "responses": {
          "200": {
            "description": "پاسخِ JSON"
          }
        }
      }
    },
    "/api/admin/passkey/login/finish": {
      "post": {
        "tags": [
          "مدیریت"
        ],
        "summary": "تکمیلِ ورود با Passkey",
        "description": "پاسخِ WebAuthn را راستی‌آزمایی و نشستِ ورود را نهایی می‌کند.",
        "responses": {
          "200": {
            "description": "پاسخِ JSON"
          }
        }
      }
    },
    "/api/admin/passkey/register/begin": {
      "get": {
        "tags": [
          "مدیریت"
        ],
        "summary": "آغازِ ثبتِ Passkey",
        "description": "چالشِ WebAuthn برای ثبتِ کلیدِ عبورِ جدید را می‌سازد.",
        "responses": {
          "200": {
            "description": "پاسخِ JSON"
          },
          "401": {
            "description": "نیازمندِ احراز هویت"
          },
          "403": {
            "description": "دسترسیِ مدیریتی لازم است"
          }
        }
      }
    },
    "/api/admin/passkey/register/finish": {
      "get": {
        "tags": [
          "مدیریت"
        ],
        "summary": "تکمیلِ ثبتِ Passkey",
        "description": "پاسخِ ثبتِ WebAuthn را راستی‌آزمایی و کلیدِ عبور را ذخیره می‌کند.",
        "responses": {
          "200": {
            "description": "پاسخِ JSON"
          },
          "401": {
            "description": "نیازمندِ احراز هویت"
          },
          "403": {
            "description": "دسترسیِ مدیریتی لازم است"
          }
        }
      }
    },
    "/api/admin/quality": {
      "get": {
        "tags": [
          "مدیریت"
        ],
        "summary": "رویدادهای خامِ کیفیتِ داده",
        "description": "آخرین رویدادهای کیفیتِ داده به‌همراه شمارشِ هشدار و خطا. پاسخ ۶۰ ثانیه کش می‌شود. نمای کاربرپسندِ همین داده در /api/admin/data-quality است.",
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "default": 50,
              "maximum": 500
            },
            "description": "بیشینهٔ رویدادهای بازگشتی"
          }
        ],
        "responses": {
          "200": {
            "description": "پاسخِ JSON"
          },
          "401": {
            "description": "نیازمندِ احراز هویت"
          },
          "403": {
            "description": "دسترسیِ مدیریتی لازم است"
          }
        }
      }
    },
    "/api/admin/scheduled": {
      "get": {
        "tags": [
          "مدیریت"
        ],
        "summary": "فهرستِ کارهای زمان‌بندی‌شده",
        "description": "کارهای زمان‌بندی‌شدهٔ سامانه را فهرست می‌کند.",
        "responses": {
          "200": {
            "description": "پاسخِ JSON"
          },
          "401": {
            "description": "نیازمندِ احراز هویت"
          },
          "403": {
            "description": "دسترسیِ مدیریتی لازم است"
          }
        }
      }
    },
    "/api/admin/scheduled/add": {
      "post": {
        "tags": [
          "مدیریت"
        ],
        "summary": "افزودنِ کارِ زمان‌بندی‌شده",
        "description": "کارِ زمان‌بندی‌شدهٔ جدید ثبت می‌کند.",
        "responses": {
          "200": {
            "description": "پاسخِ JSON"
          },
          "401": {
            "description": "نیازمندِ احراز هویت"
          },
          "403": {
            "description": "دسترسیِ مدیریتی لازم است"
          }
        }
      }
    },
    "/api/admin/scheduled/delete": {
      "post": {
        "tags": [
          "مدیریت"
        ],
        "summary": "حذفِ کارِ زمان‌بندی‌شده",
        "description": "کارِ زمان‌بندی‌شده را حذف می‌کند.",
        "responses": {
          "200": {
            "description": "پاسخِ JSON"
          },
          "401": {
            "description": "نیازمندِ احراز هویت"
          },
          "403": {
            "description": "دسترسیِ مدیریتی لازم است"
          }
        }
      }
    },
    "/api/admin/settings": {
      "get": {
        "tags": [
          "مدیریت"
        ],
        "summary": "خواندنِ تنظیماتِ سامانه",
        "description": "تنظیماتِ عمومیِ سامانه را برمی‌گرداند. هدرِ ETag اعتبارسنجِ سندِ تنظیمات است و باید در If-Matchِ نوشتنِ بعدی پس فرستاده شود. کلیدهای با پیشوندِ «_» ردیفِ kv_config نیستند بلکه وضعیتِ محاسبه‌شدهٔ سرورند (_email_smtp_configured، _email_login_effective، _email_channel_open) و فقط‌خواندنی‌اند؛ در ETag هم شمرده نمی‌شوند.",
        "responses": {
          "200": {
            "description": "پاسخِ JSON",
            "headers": {
              "ETag": {
                "description": "اعتبارسنجِ سندِ تنظیمات برای کنترلِ همزمانیِ خوش‌بینانه",
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "401": {
            "description": "نیازمندِ احراز هویت"
          },
          "403": {
            "description": "دسترسیِ مدیریتی لازم است"
          }
        }
      }
    },
    "/api/admin/settings/set": {
      "post": {
        "tags": [
          "مدیریت"
        ],
        "summary": "تغییرِ تنظیمِ سامانه",
        "description": "یک کلیدِ تنظیماتِ سامانه را مقداردهی می‌کند. اگر هدرِ If-Match بیاید و با وضعیتِ فعلیِ تنظیمات نخواند، نوشتن انجام نمی‌شود و ۴۰۹ برمی‌گردد.",
        "responses": {
          "200": {
            "description": "پاسخِ JSON — شاملِ etagِ تازه برای نوشتنِ بعدی"
          },
          "401": {
            "description": "نیازمندِ احراز هویت"
          },
          "403": {
            "description": "دسترسیِ مدیریتی لازم است"
          },
          "409": {
            "description": "تنظیمات از زمانِ خواندن تغییر کرده است (If-Match نخواند)"
          }
        },
        "parameters": [
          {
            "name": "If-Match",
            "in": "header",
            "required": false,
            "description": "ETagِ خوانده‌شده از GET /api/admin/settings؛ جلوی بازنویسیِ بی‌صدای تغییرِ ادمینِ دیگر را می‌گیرد.",
            "schema": {
              "type": "string"
            }
          }
        ]
      }
    },
    "/api/admin/stats": {
      "get": {
        "tags": [
          "مدیریت"
        ],
        "summary": "آمارِ مدیریتیِ سامانه",
        "description": "آمارِ پایگاه‌داده، تعدادِ اشتراک‌های Push، Passkeyها و آمادگیِ VAPID/WebAuthn.",
        "responses": {
          "200": {
            "description": "پاسخِ JSON"
          },
          "401": {
            "description": "نیازمندِ احراز هویت"
          },
          "403": {
            "description": "دسترسیِ مدیریتی لازم است"
          }
        }
      }
    },
    "/api/admin/strategy-ops": {
      "post": {
        "tags": [
          "مدیریت"
        ],
        "summary": "عملیاتِ مدیریتیِ رجیستریِ استراتژی",
        "description": "اقدامِ مدیریتی روی رجیستریِ استراتژی‌ها بر پایهٔ action/slug/reason.",
        "responses": {
          "200": {
            "description": "پاسخِ JSON"
          },
          "401": {
            "description": "نیازمندِ احراز هویت"
          },
          "403": {
            "description": "دسترسیِ مدیریتی لازم است"
          }
        }
      }
    },
    "/api/admin/system/status": {
      "get": {
        "tags": [
          "بازار"
        ],
        "summary": "وضعیت فنی کامل سامانه (فقط ادمین)",
        "description": "نمای کامل عملیاتی: آمار موتور دیتابیس، تازگی و حجم feedها و وضعیت jobهای نگهداری Timescale. نیازمند JWT با نقش admin است؛ در غیر این صورت 403 برمی‌گردد.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "پاسخِ JSON",
            "content": {
              "application/json": {
                "example": {
                  "db": {
                    "hit_pct": 99,
                    "numbackends": 10,
                    "xact_commit": 6302794,
                    "deadlocks": 53,
                    "temp_bytes": "187 GB"
                  },
                  "freshness": [
                    {
                      "feed": "latest",
                      "max_ts": "2026-07-28 09:13:23.216318+00",
                      "rows": 4117
                    }
                  ],
                  "jobs": [
                    {
                      "job_id": 1001,
                      "hypertable_name": "orderbook_history",
                      "job_status": "Scheduled",
                      "last_run_status": "Success",
                      "total_runs": 32,
                      "total_failures": 1
                    }
                  ]
                }
              }
            }
          },
          "403": {
            "description": "دسترسی ادمین لازم است"
          }
        }
      }
    },
    "/api/admin/totp/setup": {
      "post": {
        "tags": [
          "مدیریت"
        ],
        "summary": "رازِ تازه‌ای برای ورودِ دومرحله‌ای می‌سازد و رشته‌ی otpauth را برمی‌گرداند",
        "description": "رازِ تازه‌ای برای ورودِ دومرحله‌ای می‌سازد و رشته‌ی otpauth را برمی‌گرداند. راز persist نمی‌شود؛ ادمین باید آن را در متغیرِ TSETMC_ADMIN_TOTP_SECRET بگذارد و سرور را ری‌استارت کند.",
        "responses": {
          "200": {
            "description": "پاسخِ JSON"
          },
          "401": {
            "description": "نیازمندِ احراز هویت"
          },
          "403": {
            "description": "دسترسیِ مدیریتی لازم است"
          }
        }
      }
    },
    "/api/admin/users": {
      "get": {
        "tags": [
          "مدیریت"
        ],
        "summary": "فهرستِ کاربران",
        "description": "کاربرانِ سامانه را فهرست می‌کند.",
        "responses": {
          "200": {
            "description": "پاسخِ JSON"
          },
          "401": {
            "description": "نیازمندِ احراز هویت"
          },
          "403": {
            "description": "دسترسیِ مدیریتی لازم است"
          }
        }
      }
    },
    "/api/admin/users/ban": {
      "post": {
        "tags": [
          "مدیریت"
        ],
        "summary": "مسدودسازیِ کاربر",
        "description": "کاربر را مسدود یا رفعِ‌مسدود می‌کند.",
        "responses": {
          "200": {
            "description": "پاسخِ JSON"
          },
          "401": {
            "description": "نیازمندِ احراز هویت"
          },
          "403": {
            "description": "دسترسیِ مدیریتی لازم است"
          }
        }
      }
    },
    "/api/admin/users/export": {
      "post": {
        "tags": [
          "مدیریت"
        ],
        "summary": "برون‌بریِ فهرستِ کاربران",
        "description": "فهرستِ کاربران را برای برون‌بری خروجی می‌دهد.",
        "responses": {
          "200": {
            "description": "پاسخِ JSON"
          },
          "401": {
            "description": "نیازمندِ احراز هویت"
          },
          "403": {
            "description": "دسترسیِ مدیریتی لازم است"
          }
        }
      }
    },
    "/api/admin/users/logout": {
      "post": {
        "tags": [
          "مدیریت"
        ],
        "summary": "خروجِ اجباریِ کاربر",
        "description": "همهٔ نشست‌های یک کاربر را باطل می‌کند.",
        "responses": {
          "200": {
            "description": "پاسخِ JSON"
          },
          "401": {
            "description": "نیازمندِ احراز هویت"
          },
          "403": {
            "description": "دسترسیِ مدیریتی لازم است"
          }
        }
      }
    },
    "/api/admin/users/overview": {
      "get": {
        "tags": [
          "مدیریت"
        ],
        "summary": "آمارِ کلیِ کاربران",
        "description": "آمارِ کلیِ کاربران",
        "responses": {
          "200": {
            "description": "پاسخِ JSON"
          },
          "401": {
            "description": "نیازمندِ احراز هویت"
          },
          "403": {
            "description": "دسترسیِ مدیریتی لازم است"
          }
        }
      }
    },
    "/api/admin/users/plan": {
      "post": {
        "tags": [
          "مدیریت"
        ],
        "summary": "تغییرِ پلنِ کاربر",
        "description": "پلنِ اشتراکِ کاربر را تغییر می‌دهد.",
        "responses": {
          "200": {
            "description": "پاسخِ JSON"
          },
          "401": {
            "description": "نیازمندِ احراز هویت"
          },
          "403": {
            "description": "دسترسیِ مدیریتی لازم است"
          }
        }
      }
    },
    "/api/admin/users/role": {
      "post": {
        "tags": [
          "مدیریت"
        ],
        "summary": "تغییرِ نقشِ کاربر",
        "description": "نقشِ دسترسیِ کاربر را تغییر می‌دهد.",
        "responses": {
          "200": {
            "description": "پاسخِ JSON"
          },
          "401": {
            "description": "نیازمندِ احراز هویت"
          },
          "403": {
            "description": "دسترسیِ مدیریتی لازم است"
          }
        }
      }
    },
    "/api/admin/users/search": {
      "get": {
        "tags": [
          "مدیریت"
        ],
        "summary": "جست‌وجوی کاربران",
        "description": "کاربران را بر پایهٔ عبارتِ جست‌وجو می‌یابد.",
        "responses": {
          "200": {
            "description": "پاسخِ JSON"
          },
          "401": {
            "description": "نیازمندِ احراز هویت"
          },
          "403": {
            "description": "دسترسیِ مدیریتی لازم است"
          }
        }
      }
    },
    "/api/collect": {
      "post": {
        "tags": [
          "ابزار"
        ],
        "summary": "دریافتِ بیکنِ تحلیلِ بازدید",
        "description": "رویدادِ بازدید/درگیری/سفارشیِ سایت را از ردیابِ اولِ‌شخص (`zf-collect.js`) می‌گیرد. بدنه کلیدهای کوتاه دارد (`k` نوعِ رویداد: pv | en | ev). **همیشه ۲۰۰ برمی‌گرداند** (جز محدودیتِ نرخ) چون `navigator.sendBeacon` خواننده‌ای برای پاسخ ندارد و خطا دادن فقط کنسولِ مرورگر را کثیف می‌کرد؛ رویدادِ رد‌شده (ربات، بدنهٔ نامعتبر، صفِ پر) هم بی‌صدا دور ریخته می‌شود. `Content-Type` باید `application/json` باشد — بدنهٔ `sendBeacon` را باید در `Blob` با همین نوع پیچید وگرنه ۴۱۵ می‌گیرد. هدرِ `Authorization` اختیاری است و اگر بیاید رویداد به کاربر نسبت داده می‌شود.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "k"
                ],
                "properties": {
                  "k": {
                    "type": "string",
                    "enum": [
                      "pv",
                      "en",
                      "ev"
                    ],
                    "description": "نوعِ رویداد: بازدیدِ صفحه | درگیری (زمانِ فعال و عمقِ پیمایشِ لحظهٔ ترک) | رویدادِ سفارشی"
                  },
                  "vi": {
                    "type": "string",
                    "description": "شناسهٔ بازدیدکننده (ساختهٔ مرورگر)"
                  },
                  "si": {
                    "type": "string",
                    "description": "شناسهٔ نشست؛ ۳۰ دقیقه بی‌کنشی نشستِ تازه می‌سازد"
                  },
                  "p": {
                    "type": "string",
                    "description": "مسیرِ صفحه (سرور کوئری و هش را می‌بُرد)"
                  },
                  "t": {
                    "type": "string",
                    "description": "عنوانِ صفحه"
                  },
                  "r": {
                    "type": "string",
                    "description": "نشانیِ ارجاع‌دهنده؛ ارجاعِ هم‌دامنه دور ریخته می‌شود"
                  },
                  "q": {
                    "type": "string",
                    "description": "رشتهٔ کوئریِ صفحه — منبعِ استخراجِ `utm_*` و `gclid`/`fbclid`"
                  },
                  "pp": {
                    "type": "string",
                    "description": "مسیرِ صفحهٔ قبلی (برای نمودارِ جریان)"
                  },
                  "d": {
                    "type": "string",
                    "description": "دستگاه: desktop | mobile | tablet"
                  },
                  "o": {
                    "type": "string",
                    "description": "سیستم‌عامل"
                  },
                  "b": {
                    "type": "string",
                    "description": "مرورگر"
                  },
                  "bv": {
                    "type": "string",
                    "description": "نسخهٔ مرورگر"
                  },
                  "sw": {
                    "type": "integer",
                    "description": "پهنای نمایشگر"
                  },
                  "sh": {
                    "type": "integer",
                    "description": "ارتفاعِ نمایشگر"
                  },
                  "vw": {
                    "type": "integer",
                    "description": "پهنای viewport"
                  },
                  "l": {
                    "type": "string",
                    "description": "زبانِ مرورگر"
                  },
                  "tz": {
                    "type": "string",
                    "description": "منطقهٔ زمانیِ مرورگر"
                  },
                  "n": {
                    "type": "boolean",
                    "description": "بازدیدکنندهٔ تازه"
                  },
                  "e": {
                    "type": "boolean",
                    "description": "صفحهٔ ورودِ نشست"
                  },
                  "pi": {
                    "type": "integer",
                    "description": "شمارهٔ بازدید در نشست"
                  },
                  "lm": {
                    "type": "integer",
                    "description": "زمانِ بارگذاری (ms)"
                  },
                  "dm": {
                    "type": "integer",
                    "description": "زمانِ **فعال** روی صفحه (ms) — وقتی تب پنهان شود شمارنده می‌ایستد"
                  },
                  "sp": {
                    "type": "integer",
                    "description": "بیشینهٔ عمقِ پیمایش (درصد)"
                  },
                  "nm": {
                    "type": "string",
                    "description": "نامِ رویدادِ سفارشی (`k=ev`)"
                  },
                  "v": {
                    "type": "number",
                    "description": "مقدارِ عددیِ رویدادِ سفارشی"
                  },
                  "pr": {
                    "type": "object",
                    "description": "ویژگی‌های دلخواهِ رویداد (JSON، حداکثر ۱۰۲۴ بایت)"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "پذیرفته شد (یا بی‌صدا دور ریخته شد)"
          },
          "415": {
            "description": "`Content-Type` غیرِ JSON"
          },
          "429": {
            "description": "عبور از محدودیتِ نرخ (۱۲۰ رویداد در هر ۵ دقیقه برای هر IP)"
          }
        }
      }
    },
    "/api/admin/analytics/summary": {
      "get": {
        "tags": [
          "مدیریت"
        ],
        "summary": "خلاصهٔ تحلیلِ بازدید",
        "description": "دوازده سنجهٔ کلیدیِ بازه (بازدیدکننده، تازه، نشست، بازدیدِ صفحه، صفحه در هر نشست، نرخِ پرش، زمانِ فعال، طولِ نشست، کاربرِ واردشده، رویداد، بارگذاری، عمقِ پیمایش) به‌همراهِ همان سنجه‌ها برای **دورهٔ هم‌طولِ قبل** در کلیدِ `previous` برای محاسبهٔ درصدِ تغییر. کلیدِ `ingest` وضعیتِ صفِ درج را می‌دهد (`queued`/`stored`/`dropped`/`bots`) — تنها نشانهٔ «آمار دارد کم ثبت می‌شود»؛ بی‌آن، افتِ ناگهانیِ اعداد به کاهشِ ترافیک تعبیر می‌شد.",
        "responses": {
          "200": {
            "description": "پاسخِ JSON"
          },
          "401": {
            "description": "نیازمندِ احراز هویت"
          },
          "403": {
            "description": "دسترسیِ مدیریتی لازم است"
          }
        },
        "parameters": [
          {
            "name": "from",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "تاریخِ آغاز به وقتِ تهران (YYYY-MM-DD). با `to` می‌آید و بر `days` مقدم است."
          },
          {
            "name": "to",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "تاریخِ پایان به وقتِ تهران (YYYY-MM-DD). سرور آن را بازهٔ باز می‌کند (یک روز جلو) تا آخرین روز نیمه‌خالی نباشد."
          },
          {
            "name": "days",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "تعدادِ روزِ اخیر (پیش‌فرض ۷، بیشینه ۷۳۰). فقط وقتی `from`/`to` نیامده باشند."
          },
          {
            "name": "path",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "محدودکردن به یک مسیرِ صفحه، مثلاً `/symbol.html`."
          },
          {
            "name": "device",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "دستگاه: desktop | mobile | tablet"
          },
          {
            "name": "source",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "منبعِ ورودی، مثلاً google یا telegram"
          },
          {
            "name": "browser",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "نامِ مرورگر"
          },
          {
            "name": "os",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "سیستم‌عاملِ بازدیدکننده"
          },
          {
            "name": "auth",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "وضعیتِ ورود: auth (واردشده) | anon (ناشناس) | خالی (همه)"
          }
        ]
      }
    },
    "/api/admin/analytics/series": {
      "get": {
        "tags": [
          "مدیریت"
        ],
        "summary": "سریِ زمانیِ بازدید",
        "description": "بازدیدکننده/نشست/بازدیدِ صفحه/بازدیدکنندهٔ تازه در سطل‌های زمانی به وقتِ تهران. بدونِ `bucket`، سرور خودش انتخاب می‌کند: تا ۲ روز ساعتی، تا ۹۲ روز روزانه، بیشتر هفتگی.",
        "responses": {
          "200": {
            "description": "پاسخِ JSON"
          },
          "401": {
            "description": "نیازمندِ احراز هویت"
          },
          "403": {
            "description": "دسترسیِ مدیریتی لازم است"
          }
        },
        "parameters": [
          {
            "name": "from",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "تاریخِ آغاز به وقتِ تهران (YYYY-MM-DD). با `to` می‌آید و بر `days` مقدم است."
          },
          {
            "name": "to",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "تاریخِ پایان به وقتِ تهران (YYYY-MM-DD). سرور آن را بازهٔ باز می‌کند (یک روز جلو) تا آخرین روز نیمه‌خالی نباشد."
          },
          {
            "name": "days",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "تعدادِ روزِ اخیر (پیش‌فرض ۷، بیشینه ۷۳۰). فقط وقتی `from`/`to` نیامده باشند."
          },
          {
            "name": "path",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "محدودکردن به یک مسیرِ صفحه، مثلاً `/symbol.html`."
          },
          {
            "name": "device",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "دستگاه: desktop | mobile | tablet"
          },
          {
            "name": "source",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "منبعِ ورودی، مثلاً google یا telegram"
          },
          {
            "name": "browser",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "نامِ مرورگر"
          },
          {
            "name": "os",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "سیستم‌عاملِ بازدیدکننده"
          },
          {
            "name": "auth",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "وضعیتِ ورود: auth (واردشده) | anon (ناشناس) | خالی (همه)"
          },
          {
            "name": "bucket",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "گامِ زمانی — فهرستِ سفید: minute | hour | day | week | month. خالی = خودکار."
          }
        ]
      }
    },
    "/api/admin/analytics/breakdown": {
      "get": {
        "tags": [
          "مدیریت"
        ],
        "summary": "تفکیکِ بازدید بر حسبِ یک بُعد",
        "description": "برای هر مقدارِ بُعد، بازدید/بازدیدکننده/نشست/زمانِ فعال/عمقِ پیمایش/نرخِ پرش. بُعدها **فهرستِ سفید**اند و نامِ ستون هرگز از ورودیِ کاربر ساخته نمی‌شود؛ فهرستِ مجاز در کلیدِ `dims` پاسخ می‌آید.",
        "responses": {
          "200": {
            "description": "پاسخِ JSON"
          },
          "401": {
            "description": "نیازمندِ احراز هویت"
          },
          "403": {
            "description": "دسترسیِ مدیریتی لازم است"
          }
        },
        "parameters": [
          {
            "name": "from",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "تاریخِ آغاز به وقتِ تهران (YYYY-MM-DD). با `to` می‌آید و بر `days` مقدم است."
          },
          {
            "name": "to",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "تاریخِ پایان به وقتِ تهران (YYYY-MM-DD). سرور آن را بازهٔ باز می‌کند (یک روز جلو) تا آخرین روز نیمه‌خالی نباشد."
          },
          {
            "name": "days",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "تعدادِ روزِ اخیر (پیش‌فرض ۷، بیشینه ۷۳۰). فقط وقتی `from`/`to` نیامده باشند."
          },
          {
            "name": "path",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "محدودکردن به یک مسیرِ صفحه، مثلاً `/symbol.html`."
          },
          {
            "name": "device",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "دستگاه: desktop | mobile | tablet"
          },
          {
            "name": "source",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "منبعِ ورودی، مثلاً google یا telegram"
          },
          {
            "name": "browser",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "نامِ مرورگر"
          },
          {
            "name": "os",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "سیستم‌عاملِ بازدیدکننده"
          },
          {
            "name": "auth",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "وضعیتِ ورود: auth (واردشده) | anon (ناشناس) | خالی (همه)"
          },
          {
            "name": "dim",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "بُعد: path | title | ref_host | source | medium | campaign | term | device | os | browser | lang | tz | country | screen | name"
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "حداکثر تعدادِ ردیف (پیش‌فرض ۲۰)"
          }
        ]
      }
    },
    "/api/admin/analytics/pages": {
      "get": {
        "tags": [
          "مدیریت"
        ],
        "summary": "صفحات — پربازدید، ورود، خروج، سرعت",
        "description": "چهار فهرست در یک پاسخ: `top` (پربازدیدترین)، `entry` (صفحهٔ ورودِ نشست)، `exit` (آخرین بازدیدِ هر نشست)، و `perf` (میانگین و صدکِ ۹۰ زمانِ بارگذاری).",
        "responses": {
          "200": {
            "description": "پاسخِ JSON"
          },
          "401": {
            "description": "نیازمندِ احراز هویت"
          },
          "403": {
            "description": "دسترسیِ مدیریتی لازم است"
          }
        },
        "parameters": [
          {
            "name": "from",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "تاریخِ آغاز به وقتِ تهران (YYYY-MM-DD). با `to` می‌آید و بر `days` مقدم است."
          },
          {
            "name": "to",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "تاریخِ پایان به وقتِ تهران (YYYY-MM-DD). سرور آن را بازهٔ باز می‌کند (یک روز جلو) تا آخرین روز نیمه‌خالی نباشد."
          },
          {
            "name": "days",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "تعدادِ روزِ اخیر (پیش‌فرض ۷، بیشینه ۷۳۰). فقط وقتی `from`/`to` نیامده باشند."
          },
          {
            "name": "path",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "محدودکردن به یک مسیرِ صفحه، مثلاً `/symbol.html`."
          },
          {
            "name": "device",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "دستگاه: desktop | mobile | tablet"
          },
          {
            "name": "source",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "منبعِ ورودی، مثلاً google یا telegram"
          },
          {
            "name": "browser",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "نامِ مرورگر"
          },
          {
            "name": "os",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "سیستم‌عاملِ بازدیدکننده"
          },
          {
            "name": "auth",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "وضعیتِ ورود: auth (واردشده) | anon (ناشناس) | خالی (همه)"
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "حداکثر تعدادِ ردیفِ هر فهرست"
          }
        ]
      }
    },
    "/api/admin/analytics/flow": {
      "get": {
        "tags": [
          "مدیریت"
        ],
        "summary": "جریانِ حرکت بینِ صفحات",
        "description": "پرتکرارترین گذارهای صفحه‌به‌صفحه (`from`/`to`/`count`) برای نمودارِ سنکی.",
        "responses": {
          "200": {
            "description": "پاسخِ JSON"
          },
          "401": {
            "description": "نیازمندِ احراز هویت"
          },
          "403": {
            "description": "دسترسیِ مدیریتی لازم است"
          }
        },
        "parameters": [
          {
            "name": "from",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "تاریخِ آغاز به وقتِ تهران (YYYY-MM-DD). با `to` می‌آید و بر `days` مقدم است."
          },
          {
            "name": "to",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "تاریخِ پایان به وقتِ تهران (YYYY-MM-DD). سرور آن را بازهٔ باز می‌کند (یک روز جلو) تا آخرین روز نیمه‌خالی نباشد."
          },
          {
            "name": "days",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "تعدادِ روزِ اخیر (پیش‌فرض ۷، بیشینه ۷۳۰). فقط وقتی `from`/`to` نیامده باشند."
          },
          {
            "name": "path",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "محدودکردن به یک مسیرِ صفحه، مثلاً `/symbol.html`."
          },
          {
            "name": "device",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "دستگاه: desktop | mobile | tablet"
          },
          {
            "name": "source",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "منبعِ ورودی، مثلاً google یا telegram"
          },
          {
            "name": "browser",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "نامِ مرورگر"
          },
          {
            "name": "os",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "سیستم‌عاملِ بازدیدکننده"
          },
          {
            "name": "auth",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "وضعیتِ ورود: auth (واردشده) | anon (ناشناس) | خالی (همه)"
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "حداکثر تعدادِ گذار"
          }
        ]
      }
    },
    "/api/admin/analytics/engagement": {
      "get": {
        "tags": [
          "مدیریت"
        ],
        "summary": "درگیریِ بازدیدکننده",
        "description": "چهار توزیع در یک پاسخ: `scroll` (عمقِ پیمایش)، `dwell` (زمانِ فعال روی صفحه)، `depth` (تعدادِ صفحه در نشست)، و `heat` (ماتریسِ روز×ساعت به وقتِ تهران؛ `dow=0` شنبه است تا هفته ایرانی باشد).",
        "responses": {
          "200": {
            "description": "پاسخِ JSON"
          },
          "401": {
            "description": "نیازمندِ احراز هویت"
          },
          "403": {
            "description": "دسترسیِ مدیریتی لازم است"
          }
        },
        "parameters": [
          {
            "name": "from",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "تاریخِ آغاز به وقتِ تهران (YYYY-MM-DD). با `to` می‌آید و بر `days` مقدم است."
          },
          {
            "name": "to",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "تاریخِ پایان به وقتِ تهران (YYYY-MM-DD). سرور آن را بازهٔ باز می‌کند (یک روز جلو) تا آخرین روز نیمه‌خالی نباشد."
          },
          {
            "name": "days",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "تعدادِ روزِ اخیر (پیش‌فرض ۷، بیشینه ۷۳۰). فقط وقتی `from`/`to` نیامده باشند."
          },
          {
            "name": "path",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "محدودکردن به یک مسیرِ صفحه، مثلاً `/symbol.html`."
          },
          {
            "name": "device",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "دستگاه: desktop | mobile | tablet"
          },
          {
            "name": "source",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "منبعِ ورودی، مثلاً google یا telegram"
          },
          {
            "name": "browser",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "نامِ مرورگر"
          },
          {
            "name": "os",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "سیستم‌عاملِ بازدیدکننده"
          },
          {
            "name": "auth",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "وضعیتِ ورود: auth (واردشده) | anon (ناشناس) | خالی (همه)"
          }
        ]
      }
    },
    "/api/admin/analytics/realtime": {
      "get": {
        "tags": [
          "مدیریت"
        ],
        "summary": "بازدیدکنندگانِ همین حالا",
        "description": "پنجرهٔ کوتاهِ اخیر: شمارِ بازدیدکننده و نشستِ فعال، سریِ دقیقه‌به‌دقیقه، فهرستِ زندهٔ رویدادها، و صفحات/منابعِ فعال. **عمداً از صافیِ گزارش پیروی نمی‌کند** — بازه فقط با `mins` تعیین می‌شود، وگرنه انتخابِ «۹۰ روز» در پنل یک «همین حالا»یِ ۹۰روزه می‌ساخت. سریِ خروجی فقط دقیقه‌های **دارای رویداد** را دارد (نتیجهٔ `GROUP BY`)؛ پرکردنِ سطل‌های خالی کارِ کلاینت است.",
        "responses": {
          "200": {
            "description": "پاسخِ JSON"
          },
          "401": {
            "description": "نیازمندِ احراز هویت"
          },
          "403": {
            "description": "دسترسیِ مدیریتی لازم است"
          }
        },
        "parameters": [
          {
            "name": "mins",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "طولِ پنجره به دقیقه (پیش‌فرض ۳۰، بیشینه ۱۸۰)"
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "حداکثر تعدادِ ردیفِ فهرستِ زنده"
          }
        ]
      }
    },
    "/api/admin/analytics/users": {
      "get": {
        "tags": [
          "مدیریت"
        ],
        "summary": "فعال‌ترین کاربرانِ واردشده",
        "description": "بازدید/نشست/روزِ فعال/زمانِ فعال/آخرین بازدیدِ هر کاربرِ واردشده، با پیوند به جدولِ `users`. همان چیزی که در GA4 وجود ندارد و یکی از دلیل‌های ساختنِ این لایه بود.",
        "responses": {
          "200": {
            "description": "پاسخِ JSON"
          },
          "401": {
            "description": "نیازمندِ احراز هویت"
          },
          "403": {
            "description": "دسترسیِ مدیریتی لازم است"
          }
        },
        "parameters": [
          {
            "name": "from",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "تاریخِ آغاز به وقتِ تهران (YYYY-MM-DD). با `to` می‌آید و بر `days` مقدم است."
          },
          {
            "name": "to",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "تاریخِ پایان به وقتِ تهران (YYYY-MM-DD). سرور آن را بازهٔ باز می‌کند (یک روز جلو) تا آخرین روز نیمه‌خالی نباشد."
          },
          {
            "name": "days",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "تعدادِ روزِ اخیر (پیش‌فرض ۷، بیشینه ۷۳۰). فقط وقتی `from`/`to` نیامده باشند."
          },
          {
            "name": "path",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "محدودکردن به یک مسیرِ صفحه، مثلاً `/symbol.html`."
          },
          {
            "name": "device",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "دستگاه: desktop | mobile | tablet"
          },
          {
            "name": "source",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "منبعِ ورودی، مثلاً google یا telegram"
          },
          {
            "name": "browser",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "نامِ مرورگر"
          },
          {
            "name": "os",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "سیستم‌عاملِ بازدیدکننده"
          },
          {
            "name": "auth",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "وضعیتِ ورود: auth (واردشده) | anon (ناشناس) | خالی (همه)"
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "حداکثر تعدادِ کاربر"
          }
        ]
      }
    },
    "/api/admin/analytics/events": {
      "get": {
        "tags": [
          "مدیریت"
        ],
        "summary": "رویدادهای سفارشی",
        "description": "تعداد/بازدیدکننده/نشست/جمعِ مقدارِ هر رویدادِ سفارشی. رویدادها را صفحات با `window.ZFTrack(name, value, props)` می‌فرستند.",
        "responses": {
          "200": {
            "description": "پاسخِ JSON"
          },
          "401": {
            "description": "نیازمندِ احراز هویت"
          },
          "403": {
            "description": "دسترسیِ مدیریتی لازم است"
          }
        },
        "parameters": [
          {
            "name": "from",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "تاریخِ آغاز به وقتِ تهران (YYYY-MM-DD). با `to` می‌آید و بر `days` مقدم است."
          },
          {
            "name": "to",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "تاریخِ پایان به وقتِ تهران (YYYY-MM-DD). سرور آن را بازهٔ باز می‌کند (یک روز جلو) تا آخرین روز نیمه‌خالی نباشد."
          },
          {
            "name": "days",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "تعدادِ روزِ اخیر (پیش‌فرض ۷، بیشینه ۷۳۰). فقط وقتی `from`/`to` نیامده باشند."
          },
          {
            "name": "path",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "محدودکردن به یک مسیرِ صفحه، مثلاً `/symbol.html`."
          },
          {
            "name": "device",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "دستگاه: desktop | mobile | tablet"
          },
          {
            "name": "source",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "منبعِ ورودی، مثلاً google یا telegram"
          },
          {
            "name": "browser",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "نامِ مرورگر"
          },
          {
            "name": "os",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "سیستم‌عاملِ بازدیدکننده"
          },
          {
            "name": "auth",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "وضعیتِ ورود: auth (واردشده) | anon (ناشناس) | خالی (همه)"
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "حداکثر تعدادِ رویداد"
          }
        ]
      }
    },
    "/api/admin/analytics/retention": {
      "get": {
        "tags": [
          "مدیریت"
        ],
        "summary": "ماندگاریِ کوهورتی",
        "description": "ماتریسِ بازگشتِ بازدیدکننده بر مبنایِ نخستین روزِ دیده‌شدنِ او **درونِ همان بازه**. «روزِ نخست» عمداً از خودِ بازه استنتاج می‌شود نه از پرچمِ `is_new`، چون کسی که ماه‌ها پیش تازه بوده در این بازه تازه نیست.",
        "responses": {
          "200": {
            "description": "پاسخِ JSON"
          },
          "401": {
            "description": "نیازمندِ احراز هویت"
          },
          "403": {
            "description": "دسترسیِ مدیریتی لازم است"
          }
        },
        "parameters": [
          {
            "name": "from",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "تاریخِ آغاز به وقتِ تهران (YYYY-MM-DD). با `to` می‌آید و بر `days` مقدم است."
          },
          {
            "name": "to",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "تاریخِ پایان به وقتِ تهران (YYYY-MM-DD). سرور آن را بازهٔ باز می‌کند (یک روز جلو) تا آخرین روز نیمه‌خالی نباشد."
          },
          {
            "name": "days",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "تعدادِ روزِ اخیر (پیش‌فرض ۷، بیشینه ۷۳۰). فقط وقتی `from`/`to` نیامده باشند."
          },
          {
            "name": "path",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "محدودکردن به یک مسیرِ صفحه، مثلاً `/symbol.html`."
          },
          {
            "name": "device",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "دستگاه: desktop | mobile | tablet"
          },
          {
            "name": "source",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "منبعِ ورودی، مثلاً google یا telegram"
          },
          {
            "name": "browser",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "نامِ مرورگر"
          },
          {
            "name": "os",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "سیستم‌عاملِ بازدیدکننده"
          },
          {
            "name": "auth",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "وضعیتِ ورود: auth (واردشده) | anon (ناشناس) | خالی (همه)"
          },
          {
            "name": "window",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "طولِ پنجرهٔ پیگیری به روز (پیش‌فرض ۱۴، بیشینه ۶۰)"
          }
        ]
      }
    },
    "/api/admin/analytics/options": {
      "get": {
        "tags": [
          "مدیریت"
        ],
        "summary": "مقدارهای موجودِ منوهای صافی",
        "description": "مقدارهای واقعاً دیده‌شدهٔ هر بُعد (device/browser/os/source/country/campaign) در بازه، برای پُر کردنِ منوهای صافیِ پنل.",
        "responses": {
          "200": {
            "description": "پاسخِ JSON"
          },
          "401": {
            "description": "نیازمندِ احراز هویت"
          },
          "403": {
            "description": "دسترسیِ مدیریتی لازم است"
          }
        },
        "parameters": [
          {
            "name": "from",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "تاریخِ آغاز به وقتِ تهران (YYYY-MM-DD). با `to` می‌آید و بر `days` مقدم است."
          },
          {
            "name": "to",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "تاریخِ پایان به وقتِ تهران (YYYY-MM-DD). سرور آن را بازهٔ باز می‌کند (یک روز جلو) تا آخرین روز نیمه‌خالی نباشد."
          },
          {
            "name": "days",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "تعدادِ روزِ اخیر (پیش‌فرض ۷، بیشینه ۷۳۰). فقط وقتی `from`/`to` نیامده باشند."
          },
          {
            "name": "path",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "محدودکردن به یک مسیرِ صفحه، مثلاً `/symbol.html`."
          },
          {
            "name": "device",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "دستگاه: desktop | mobile | tablet"
          },
          {
            "name": "source",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "منبعِ ورودی، مثلاً google یا telegram"
          },
          {
            "name": "browser",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "نامِ مرورگر"
          },
          {
            "name": "os",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "سیستم‌عاملِ بازدیدکننده"
          },
          {
            "name": "auth",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "وضعیتِ ورود: auth (واردشده) | anon (ناشناس) | خالی (همه)"
          }
        ]
      }
    },
    "/api/arbitrage": {
      "get": {
        "tags": [
          "بازار"
        ],
        "summary": "صندوق‌های قابل‌معامله رتبه‌بندی‌شده بر پایه‌ی بزرگیِ حباب/کسرِ NAV",
        "description": "صندوق‌های قابل‌معامله رتبه‌بندی‌شده بر پایه‌ی بزرگیِ حباب/کسرِ NAV.",
        "responses": {
          "200": {
            "description": "پاسخِ JSON",
            "content": {
              "application/json": {
                "example": [
                  {
                    "symbol": "پتروما",
                    "name": "ص.س.بخشي افق دماوند1-ب",
                    "sector_name": "صندوق سرمايه گذاري قابل معامله",
                    "nav": 27363,
                    "price_last": 23327,
                    "premium": -14.75,
                    "mean_premium": -1.44,
                    "z": -4.19,
                    "hist_n": 84,
                    "change_pct": -3,
                    "value": 12848719120
                  },
                  {
                    "symbol": "نقرابي",
                    "name": "صندوق س.كالاي كهكشان فيروزه1",
                    "sector_name": "صندوق سرمايه گذاري قابل معامله",
                    "nav": 9917,
                    "price_last": 11635,
                    "premium": 17.32,
                    "mean_premium": null,
                    "z": null,
                    "hist_n": 0,
                    "change_pct": 4.82,
                    "value": 1330480039156
                  }
                ]
              }
            }
          }
        }
      }
    },
    "/api/auth/2fa/disable": {
      "post": {
        "tags": [
          "احراز هویت"
        ],
        "summary": "2FA را غیرفعال می‌کند",
        "description": "2FA را غیرفعال می‌کند.",
        "responses": {
          "200": {
            "description": "پاسخِ JSON"
          },
          "401": {
            "description": "نیازمندِ احراز هویت"
          }
        }
      }
    },
    "/api/auth/2fa/enable": {
      "post": {
        "tags": [
          "احراز هویت"
        ],
        "summary": "پس از تأییدِ یک کد، 2FA را فعال می‌کند",
        "description": "پس از تأییدِ یک کد، 2FA را فعال می‌کند.",
        "responses": {
          "200": {
            "description": "پاسخِ JSON"
          },
          "401": {
            "description": "نیازمندِ احراز هویت"
          }
        }
      }
    },
    "/api/auth/2fa/setup": {
      "post": {
        "tags": [
          "احراز هویت"
        ],
        "summary": "رازِ تازه + کدهای پشتیبان می‌سازد (هنوز فعال نه)",
        "description": "رازِ تازه + کدهای پشتیبان می‌سازد (هنوز فعال نه).",
        "responses": {
          "200": {
            "description": "پاسخِ JSON"
          },
          "401": {
            "description": "نیازمندِ احراز هویت"
          }
        }
      }
    },
    "/api/auth/captcha": {
      "get": {
        "tags": [
          "احراز هویت"
        ],
        "summary": "پیکربندیِ صفحهٔ ورود: CAPTCHA (#27) و کانال‌های در دسترس",
        "description": "وضعیتِ Turnstile و کانال‌های ورودِ پیکربندی‌شده را می‌دهد. صفحهٔ ورود پیش از هر کارِ دیگری همین را می‌گیرد و کانالِ خاموش را پنهان می‌کند تا کاربر به بن‌بست فرستاده نشود؛ به همین دلیل کانال‌ها به همین پاسخ چسبیده‌اند نه یک endpointِ جدا.",
        "responses": {
          "200": {
            "description": "وضعیتِ CAPTCHA و کانال‌های ورود",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AuthCaptchaConfig"
                },
                "example": {
                  "enabled": true,
                  "sitekey": "0x4AAAAAAA...",
                  "channels": {
                    "phone": true,
                    "email": false,
                    "telegram": false,
                    "bale": false,
                    "google": true
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/auth/email/request": {
      "post": {
        "tags": [
          "احراز هویت"
        ],
        "summary": "کدِ تأیید به ایمیلِ جدید می‌فرستد",
        "description": "کدِ تأیید به ایمیلِ جدید می‌فرستد.",
        "responses": {
          "200": {
            "description": "پاسخِ JSON"
          },
          "401": {
            "description": "نیازمندِ احراز هویت"
          }
        }
      }
    },
    "/api/auth/email/verify": {
      "post": {
        "tags": [
          "احراز هویت"
        ],
        "summary": "ایمیلِ در انتظار را نهایی می‌کند",
        "description": "ایمیلِ در انتظار را نهایی می‌کند.",
        "responses": {
          "200": {
            "description": "پاسخِ JSON"
          },
          "401": {
            "description": "نیازمندِ احراز هویت"
          }
        }
      }
    },
    "/api/auth/logout": {
      "post": {
        "tags": [
          "احراز هویت"
        ],
        "summary": "نشستِ جاری را لغو و کوکی را پاک می‌کند",
        "description": "نشستِ جاری را لغو و کوکی را پاک می‌کند.",
        "responses": {
          "200": {
            "description": "پاسخِ JSON"
          },
          "401": {
            "description": "نیازمندِ احراز هویت"
          }
        }
      }
    },
    "/api/auth/me": {
      "get": {
        "tags": [
          "احراز هویت"
        ],
        "summary": "پروفایلِ کاربرِ جاری",
        "description": "اطلاعاتِ کاربرِ احرازشدهٔ جاری را برمی‌گرداند.",
        "responses": {
          "200": {
            "description": "پاسخِ JSON"
          },
          "401": {
            "description": "نیازمندِ احراز هویت"
          }
        }
      }
    },
    "/api/auth/profile": {
      "get": {
        "tags": [
          "احراز هویت"
        ],
        "summary": "به‌روزرسانیِ نامِ کاربر",
        "description": "به‌روزرسانیِ نامِ کاربر",
        "responses": {
          "200": {
            "description": "پاسخِ JSON"
          },
          "401": {
            "description": "نیازمندِ احراز هویت"
          }
        }
      }
    },
    "/api/auth/refresh": {
      "get": {
        "tags": [
          "احراز هویت"
        ],
        "summary": "با کوکیِ refresh یک توکنِ دسترسیِ تازه می‌دهد (#1)",
        "description": "با کوکیِ refresh یک توکنِ دسترسیِ تازه می‌دهد (#1).",
        "responses": {
          "200": {
            "description": "پاسخِ JSON"
          }
        }
      }
    },
    "/api/auth/request-otp": {
      "get": {
        "tags": [
          "احراز هویت"
        ],
        "summary": "otp",
        "description": "otp",
        "responses": {
          "200": {
            "description": "پاسخِ JSON"
          }
        }
      }
    },
    "/api/auth/sessions": {
      "get": {
        "tags": [
          "احراز هویت"
        ],
        "summary": "فهرستِ دستگاه‌های واردشده (#4)",
        "description": "فهرستِ دستگاه‌های واردشده (#4).",
        "responses": {
          "200": {
            "description": "پاسخِ JSON"
          },
          "401": {
            "description": "نیازمندِ احراز هویت"
          }
        }
      }
    },
    "/api/auth/verify-otp": {
      "get": {
        "tags": [
          "احراز هویت"
        ],
        "summary": "otp",
        "description": "otp",
        "responses": {
          "200": {
            "description": "پاسخِ JSON"
          }
        }
      }
    },
    "/api/backtest/custom": {
      "post": {
        "tags": [
          "استراتژی و بک‌تست"
        ],
        "summary": "استراتژیِ سفارشیِ کاربر را اعتبارسنجی و روی جهانِ نمادهای پرمعامله بک‌تست می‌کند (کارنامه‌ی…",
        "description": "استراتژیِ سفارشیِ کاربر را اعتبارسنجی و روی جهانِ نمادهای پرمعامله بک‌تست می‌کند (کارنامه‌ی edge + سیگنال‌های امروز). قوانین JSONِ اعتبارسنجی‌شده‌اند.",
        "responses": {
          "200": {
            "description": "پاسخِ JSON"
          }
        }
      }
    },
    "/api/backtest/event": {
      "post": {
        "tags": [
          "استراتژی و بک‌تست"
        ],
        "summary": "اجرای بک‌تستِ رویدادمحور",
        "description": "موتورِ بک‌تستِ رویدادی (Event Backtest) را اجرا می‌کند — جدا از موتورِ پژوهشِ سیگنال (§1.3). نیازمندِ احراز هویت و با محدودیتِ نرخ. یا `rules` (آرایهٔ تختِ JSON) یا `dsl_slug`/`dsl` (مسیرِ preset libraryِ StrategyDSL) پر می‌شود، هرگز هر دو هم‌زمان — فقط شرطِ ورودِ DSL واردِ سیگنالِ SQL می‌شود (خروج در این موتور با مسیرِ stop/target سنجیده می‌شود، نه یک شرطِ جداگانه).",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "rules": {
                    "type": "string",
                    "description": "آرایهٔ JSONِ قوانینِ تختِ Builder (نمونه: [{\"left\":\"rsi\",\"op\":\"lt\",\"rkind\":\"num\",\"rnum\":30}]) — نمی‌تواند هم‌زمان با dsl_slug/dsl پر باشد"
                  },
                  "dsl_slug": {
                    "type": "string",
                    "description": "slugِ یک تعریفِ رجیستری (strategy_definitions.slug) که DSLِ آن lowering-able باشد؛ سرور DSL را از رجیستری می‌خواند و entry را به شرطِ SQL تبدیل می‌کند"
                  },
                  "dsl": {
                    "type": "object",
                    "description": "یک StrategyDSLِ خام (بدونِ عبور از رجیستری) — همان ساختارِ internal/store/strategy_dsl.go؛ فقط با dsl_slug ناسازگار است"
                  },
                  "market": {
                    "type": "string"
                  },
                  "days": {
                    "type": "integer"
                  },
                  "initial_cash": {
                    "type": "number"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "پاسخِ JSON"
          },
          "401": {
            "description": "نیازمندِ احراز هویت"
          }
        }
      }
    },
    "/api/backtest/event/cancel": {
      "post": {
        "tags": [
          "استراتژی و بک‌تست"
        ],
        "summary": "لغوِ کارِ بک‌تستِ رویدادی",
        "description": "کارِ در‌حالِ‌اجرای بک‌تستِ رویدادی را با job_id لغو می‌کند.",
        "responses": {
          "200": {
            "description": "پاسخِ JSON"
          },
          "401": {
            "description": "نیازمندِ احراز هویت"
          }
        }
      }
    },
    "/api/backtest/job": {
      "get": {
        "tags": [
          "استراتژی و بک‌تست"
        ],
        "summary": "وضعیتِ یک کارِ بک‌تست",
        "description": "وضعیت و نتیجهٔ یک کارِ بک‌تستِ کاربر را با شناسه برمی‌گرداند.",
        "parameters": [
          {
            "name": "id",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "شناسهٔ کارِ بک‌تست"
          }
        ],
        "responses": {
          "200": {
            "description": "پاسخِ JSON"
          },
          "401": {
            "description": "نیازمندِ احراز هویت"
          }
        }
      }
    },
    "/api/backtest/jobs": {
      "get": {
        "tags": [
          "استراتژی و بک‌تست"
        ],
        "summary": "فهرستِ کارهای بک‌تستِ کاربر",
        "description": "کارهای بک‌تستِ اخیرِ کاربرِ احرازشده را فهرست می‌کند.",
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "حداکثر تعدادِ کار"
          }
        ],
        "responses": {
          "200": {
            "description": "پاسخِ JSON"
          },
          "401": {
            "description": "نیازمندِ احراز هویت"
          }
        }
      }
    },
    "/api/backtest/optimize": {
      "post": {
        "tags": [
          "استراتژی و بک‌تست"
        ],
        "summary": "یک آستانه‌ی عددیِ یک قانون را روی شبکه بهینه می‌کند: انتخاب روی in-sample، اعتبارسنجی روی…",
        "description": "یک آستانه‌ی عددیِ یک قانون را روی شبکه بهینه می‌کند: انتخاب روی in-sample، اعتبارسنجی روی out-of-sample (نیازمندِ ورودِ کاربری + محدودیتِ نرخ).",
        "responses": {
          "200": {
            "description": "پاسخِ JSON"
          },
          "401": {
            "description": "نیازمندِ احراز هویت"
          }
        }
      }
    },
    "/api/backtest/run": {
      "post": {
        "tags": [
          "استراتژی و بک‌تست"
        ],
        "summary": "اجرای بک‌تستِ استراتژی",
        "description": "موتورِ بک‌تستِ استراتژی را اجرا می‌کند (محدودیتِ نرخ: حداکثر ۳ بک‌تست در دقیقه). یا `rules` (آرایهٔ تختِ JSON از Builder) یا `dsl_slug`/`dsl` (مسیرِ preset libraryِ StrategyDSLِ رجیستری، internal/store/strategy_dsl_lowering.go) پر می‌شود، هرگز هر دو هم‌زمان — سرور پیش از ساختنِ job با DSLIsExecutable سنجی می‌کند تا ۵۰۰ خام رخ ندهد.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "rules": {
                    "type": "string",
                    "description": "آرایهٔ JSONِ قوانینِ تختِ Builder — نمی‌تواند هم‌زمان با dsl_slug/dsl پر باشد"
                  },
                  "dsl_slug": {
                    "type": "string",
                    "description": "slugِ یک تعریفِ رجیستری که DSLِ آن lowering-able باشد؛ اگر lowering ناموفق باشد ۴۰۰ با دلیلِ فارسی برمی‌گردد، نه ۵۰۰"
                  },
                  "dsl": {
                    "type": "object",
                    "description": "یک StrategyDSLِ خام؛ فقط با dsl_slug ناسازگار است"
                  },
                  "market": {
                    "type": "string"
                  },
                  "in_sample_days": {
                    "type": "integer"
                  },
                  "oos_days": {
                    "type": "integer"
                  },
                  "fee_bps": {
                    "type": "number"
                  },
                  "slippage_bps": {
                    "type": "number"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "پاسخِ JSON"
          },
          "401": {
            "description": "نیازمندِ احراز هویت"
          }
        }
      }
    },
    "/api/backtest/walk-forward": {
      "post": {
        "tags": [
          "استراتژی و بک‌تست"
        ],
        "summary": "بک‌تستِ walk-forwardِ چرخشی را اجرا می‌کند (نیازمندِ ورودِ کاربری + محدودیتِ نرخ)",
        "description": "بک‌تستِ walk-forwardِ چرخشی را اجرا می‌کند (نیازمندِ ورودِ کاربری + محدودیتِ نرخ). هر fold یک پنجره‌ی train و یک پنجره‌ی OOS مجزا دارد؛ خروجی شاملِ پایداری و افتِ train→OOS (نشانه‌ی بیش‌برازش) است. بدونِ معامله‌ی واقعی.",
        "responses": {
          "200": {
            "description": "پاسخِ JSON"
          },
          "401": {
            "description": "نیازمندِ احراز هویت"
          }
        }
      }
    },
    "/api/breadth-advanced": {
      "get": {
        "tags": [
          "بازار"
        ],
        "summary": "سری‌زمانیِ روزانه‌ی پهنای بازار (پیشرو/پسرو/بالای MA200) را برای محاسبه‌ی مک‌کللان و Breadth…",
        "description": "سری‌زمانیِ روزانه‌ی پهنای بازار (پیشرو/پسرو/بالای MA200) را برای محاسبه‌ی مک‌کللان و Breadth Thrust در سمتِ کلاینت می‌دهد.",
        "responses": {
          "200": {
            "description": "پاسخِ JSON",
            "content": {
              "application/json": {
                "example": {
                  "rows": [
                    {
                      "d_even": 20250611,
                      "adv": 95,
                      "dec": 63,
                      "unch": 9,
                      "above200": 68,
                      "n200": 107
                    },
                    {
                      "d_even": 20250624,
                      "adv": 29,
                      "dec": 0,
                      "unch": 0,
                      "above200": 22,
                      "n200": 23
                    }
                  ]
                }
              }
            }
          }
        }
      }
    },
    "/api/breadth/history": {
      "get": {
        "tags": [
          "بازار"
        ],
        "summary": "روند پهنای بازار",
        "description": "شمار نمادهای مثبت/منفی/بدون‌تغییر، صف خرید/فروش و ارزش صف‌ها در طول نشست. پارامتر market هم در نشستِ جاری و هم در تونلِ زمان اعمال می‌شود؛ نشست‌های پیش از ۲۰۲۶-۰۷-۳۰ فقط ردیفِ «همه بازارها» دارند و برای بازارِ مشخص آرایهٔ خالی برمی‌گردانند.",
        "responses": {
          "200": {
            "description": "آرایه‌ای از نقاط پهنای بازار",
            "content": {
              "application/json": {
                "example": [
                  {
                    "ts": "2026-07-14T09:01:21.600201+03:30",
                    "positive": 604,
                    "neutral": 1339,
                    "negative": 631,
                    "buy_queue": 60,
                    "sell_queue": 156,
                    "buy_queue_value": 28696160351616,
                    "sell_queue_value": 43612032145232
                  },
                  {
                    "ts": "2026-07-14T09:01:27.621110+03:30",
                    "positive": 594,
                    "neutral": 1323,
                    "negative": 657,
                    "buy_queue": 62,
                    "sell_queue": 167,
                    "buy_queue_value": 28655556317422,
                    "sell_queue_value": 73533785770378
                  }
                ]
              }
            }
          }
        },
        "parameters": [
          {
            "name": "date",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "example": "2026-07-28"
            },
            "description": "تونل زمان: نشستِ موردنظر به‌صورتِ YYYY-MM-DD یا YYYYMMDD. بدونِ این پارامتر، نشستِ جاری برگردانده می‌شود."
          }
        ]
      }
    },
    "/api/breadth/norms": {
      "get": {
        "tags": [
          "بازار"
        ],
        "summary": "مقایسه پهنای بازار با میانگین تاریخی",
        "description": "سهمِ نمادهای مثبت در همین لحظه در برابرِ نمونه‌ای از نشست‌های گذشته. هر نشستِ گذشته در «همان ساعتِ» لحظهٔ جاری نمونه‌برداری می‌شود (نه در لحظهٔ بسته‌شدن)، چون پهنای بازار در طولِ روز مسیر دارد. نشستِ جاری از نمونه کنار گذاشته می‌شود.",
        "parameters": [
          {
            "name": "market",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "",
                "bourse",
                "farabourse",
                "tala",
                "daramad",
                "sandogh",
                "oraagh"
              ]
            },
            "description": "بازار؛ خالی یعنی همهٔ بازارها."
          },
          {
            "name": "days",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "default": 20,
              "maximum": 120
            },
            "description": "سقفِ تعدادِ نشستِ گذشته در نمونه."
          }
        ],
        "responses": {
          "200": {
            "description": "مقادیرِ جاری، آماره‌های نمونهٔ تاریخی و صدکِ امروز",
            "content": {
              "application/json": {
                "example": {
                  "market": "",
                  "as_of": "2026-07-29",
                  "at_time": "17:20",
                  "sessions": 18,
                  "positive": 806,
                  "negative": 655,
                  "neutral": 57,
                  "pos_share": 53.1,
                  "avg_positive": 829.5,
                  "avg_negative": null,
                  "avg_neutral": null,
                  "avg_pos_share": 57.3,
                  "median_pos_share": 59.3,
                  "min_pos_share": 26,
                  "max_pos_share": 86.2,
                  "percentile": 38.9
                }
              }
            }
          }
        }
      }
    },
    "/api/calendar": {
      "get": {
        "tags": [
          "بازار"
        ],
        "summary": "وضعیتِ تقویمِ معاملاتی را می‌دهد: آخرین روزِ معاملاتی، تعدادِ کل، و شکاف‌های غیرعادیِ اخیر",
        "description": "وضعیتِ تقویمِ معاملاتی را می‌دهد: آخرین روزِ معاملاتی، تعدادِ کل، و شکاف‌های غیرعادیِ اخیر. قالبِ استاندارد: {data, meta}.",
        "responses": {
          "200": {
            "description": "پاسخِ JSON",
            "content": {
              "application/json": {
                "example": {
                  "data": [
                    {
                      "from": "2026-06-23T00:00:00Z",
                      "to": "2026-06-27T00:00:00Z",
                      "gap_days": 4,
                      "trading_weekdays_in_gap": 1
                    },
                    {
                      "from": "2026-05-26T00:00:00Z",
                      "to": "2026-05-30T00:00:00Z",
                      "gap_days": 4,
                      "trading_weekdays_in_gap": 1
                    }
                  ],
                  "meta": {
                    "latest_trading_day": "2026-07-14T00:00:00Z",
                    "trading_days": 1250,
                    "gap_count": 24
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/candles": {
      "get": {
        "tags": [
          "نمادها"
        ],
        "summary": "کندل‌های قیمت",
        "parameters": [
          {
            "name": "symbol",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 200
            }
          }
        ],
        "responses": {
          "200": {
            "description": "آرایه‌ای از کندل ۱دقیقه‌ای (open/high/low/close/volume)"
          }
        }
      }
    },
    "/api/client-error": {
      "post": {
        "tags": [
          "ابزار"
        ],
        "summary": "ثبتِ خطای اجراییِ فرانت‌اند",
        "description": "گزارشِ یک خطای JavaScript از مرورگر را برای پایشِ تجمیعی می‌گیرد. هیچ داده‌ای در دیتابیس نوشته نمی‌شود؛ فقط یک شمارندهٔ درون‌حافظه‌ای با سقفِ گروه به‌روز می‌شود. عمداً همیشه ۲۰۰ برمی‌گرداند تا در صفحه‌ای که همین حالا خطا خورده، خطای دوم نسازد.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "message": {
                    "type": "string",
                    "description": "متنِ خطا (کوتاه‌شده به ۳۰۰ نویسه)"
                  },
                  "page": {
                    "type": "string",
                    "description": "مسیرِ صفحه"
                  },
                  "source": {
                    "type": "string",
                    "description": "فایلِ منبع"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "پاسخِ JSON — status برابرِ ok یا ignored"
          }
        }
      }
    },
    "/api/clienttype": {
      "get": {
        "tags": [
          "جریان پول"
        ],
        "summary": "حقیقی/حقوقی یک نماد",
        "parameters": [
          {
            "name": "symbol",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "ارزش/تعداد خرید و فروش حقیقی و حقوقی و قدرت خرید",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ClientType"
                }
              }
            }
          }
        }
      }
    },
    "/api/clienttype-history": {
      "get": {
        "tags": [
          "بازار"
        ],
        "summary": "تاریخچه‌ی روزانه‌ی جریانِ پولِ حقیقی/حقوقیِ یک نماد را می‌دهد (خالص پول حقیقی، خرید/فروشِ…",
        "description": "تاریخچه‌ی روزانه‌ی جریانِ پولِ حقیقی/حقوقیِ یک نماد را می‌دهد (خالص پول حقیقی، خرید/فروشِ حقیقی-حقوقی و سرانه‌ها). از client_type_history.",
        "responses": {
          "200": {
            "description": "پاسخِ JSON"
          }
        }
      }
    },
    "/api/codal": {
      "get": {
        "tags": [
          "بازار"
        ],
        "summary": "تازه‌ترین اطلاعیه‌های کدال",
        "description": "تازه‌ترین اطلاعیه‌های کدال؛ با پارامترِ symbol فقط همان نماد.",
        "responses": {
          "200": {
            "description": "پاسخِ JSON"
          }
        }
      }
    },
    "/api/config/public": {
      "get": {
        "tags": [
          "بازار"
        ],
        "summary": "کلیدهای site",
        "description": "کلیدهای site.* و feature.* برای فرانت‌اند.",
        "responses": {
          "200": {
            "description": "پاسخِ JSON"
          }
        }
      }
    },
    "/api/daily": {
      "get": {
        "tags": [
          "نمادها"
        ],
        "summary": "کندل‌های روزانه‌ی تاریخی",
        "description": "تاریخچه‌ی کامل کندل‌های روزانه‌ی یک نماد (از سرویس ClosingPriceDaily تی‌اس‌ای، ذخیره‌شده در جدول daily_history).",
        "parameters": [
          {
            "name": "symbol",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 500
            }
          }
        ],
        "responses": {
          "200": {
            "description": "آرایه‌ای از کندل روزانه (d_even, open/high/low/close/last/yesterday/volume/value/trade_count) به ترتیب نزولیِ تاریخ"
          }
        }
      }
    },
    "/api/daily-batch": {
      "get": {
        "tags": [
          "نمادها"
        ],
        "summary": "کندل‌های روزانه‌ی چند نماد در یک درخواست",
        "description": "دریافتِ دسته‌ایِ کندل‌های روزانه برای چند نماد (تا ۴۰ نماد) در یک فراخوانی؛ برای کاهشِ رفت‌وبرگشتِ ابزارهای مدیریت سرمایه.",
        "parameters": [
          {
            "name": "syms",
            "in": "query",
            "required": true,
            "description": "فهرستِ نمادها جداشده با کاما (حداکثر ۴۰ نماد)",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 500
            }
          },
          {
            "name": "adj",
            "in": "query",
            "description": "با مقدارِ 1 سریِ تعدیل‌شده برای رویدادهای شرکتی (افزایشِ سرمایه/سود نقدی) برمی‌گردد — همان قراردادِ /api/daily. بدونِ این پارامتر سریِ خام برمی‌گردد (رفتارِ پیش‌فرض، بدونِ تغییر).",
            "schema": {
              "type": "string",
              "enum": [
                "1"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "شیءای که کلیدِ آن نماد و مقدارِ آن آرایه‌ی کندل‌های روزانه‌ی همان نماد است (به ترتیب نزولیِ تاریخ)"
          }
        }
      }
    },
    "/api/fundamentals": {
      "get": {
        "tags": [
          "نمادها"
        ],
        "summary": "صورت‌های مالی نماد — سودوزیان",
        "description": "صورتِ سودوزیانِ نرمال‌شدهٔ یک نماد، برگرفته از صورت‌های مالی کدال، به ترتیبِ نزولیِ زمانی. واحدِ همهٔ مبالغ «میلیون ریال» است (فیلدِ `unit`)؛ تنها استثنا `eps` است که ریالِ هر سهم است. `comparable_with_prev` را سرور حساب می‌کند و می‌گوید آیا این ردیف با ردیفِ بعدیِ همین آرایه (دورهٔ پیشین) هم‌جنس است: طولِ دوره یکی باشد، فاصله دقیقاً یک دوره باشد و لنگرِ سالِ مالی جابه‌جا نشده باشد. اگر false بود، درصدِ رشد نباید نمایش داده شود. در نبودِ داده `rows` خالی با کدِ ۲۰۰ برمی‌گردد، نه ۴۰۴؛ نمادی که هنوز صورتِ مالی‌اش گرفته نشده «یافت نشد» نیست. \n\n**دورهٔ فصلی (`period=quarterly`):** کدال گزارشِ میان‌دوره‌ای را **تجمعی** منتشر می‌کند (از ابتدای سالِ مالی تا پایانِ دوره) — روی دادهٔ واقعیِ «فولاد» سالِ مالی ۱۴۰۴، درآمدِ ۶ماهه ۱٬۶۰۸٬۷۳۴٬۲۳۸ ← ۹ماهه ۲٬۴۸۸٬۰۶۴٬۴۱۴ ← ۱۲ماهه ۳٬۶۶۸٬۸۶۰٬۲۴۶. پس `quarterly` سریِ **تفاضلی** می‌دهد: هر ردیف فقط یک فصل است (`basis: \"discrete\"`، `derived: true`). `quarter` (۱..۴) و `fiscal_year` نسبت به **سالِ مالیِ خودِ شرکت**اند، نه سالِ تقویمی: برای شرکتی با سالِ مالیِ منتهی به آذر، دورهٔ منتهی به اسفند فصلِ **اول** است. اگر دو گزارشِ ورودیِ تفاضل دامنهٔ تلفیقِ ناهم‌سان داشته باشند، آن فصل **حذف** می‌شود و نه اینکه با احتیاط برگردد (تفاضلِ تلفیقی منهای شرکتِ اصلی روی دادهٔ واقعی درآمدِ منفی می‌دهد). `mixed_basis: true` مخصوصِ حالتِ خفیف‌تر است: یکی از دو پایه حسابرسی‌شده و دیگری نشده؛ عدد معتبر است ولی `audited` قطعاً false می‌شود. برای گرفتنِ عددِ **خامِ تجمعی** به‌جای تفاضلی، از `3m`/`6m`/`9m` استفاده کنید (`basis: \"cumulative\"`). پاسخِ `annual` فیلدِ `has_quarterly` را هم دارد تا رابطِ کاربری بداند کلیدِ «فصلی» برای این نماد معنا دارد یا نه.",
        "parameters": [
          {
            "name": "symbol",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "نمادِ بورسی، مثلاً «فولاد»"
          },
          {
            "name": "period",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "annual",
                "quarterly",
                "9m",
                "6m",
                "3m"
              ],
              "default": "annual"
            },
            "description": "دوره. `annual` (پیش‌فرض، سازگارِ عقب‌رو) سریِ ۱۲ماهه؛ `quarterly` سریِ فصلیِ **تفاضلی**؛ `9m`/`6m`/`3m` همان عددِ خامِ تجمعیِ کدال برای آن طولِ دوره."
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "default": 8,
              "maximum": 40
            },
            "description": "بیشینهٔ تعدادِ دوره (پیش‌فرضِ `quarterly` برابرِ ۱۶ است)"
          }
        ],
        "responses": {
          "200": {
            "description": "پاسخِ JSON",
            "content": {
              "application/json": {
                "examples": {
                  "annual": {
                    "summary": "سریِ سالانه (پیش‌فرض)",
                    "value": {
                      "symbol": "فولاد",
                      "unit": "million_rial",
                      "rows": [
                        {
                          "period_end": "1404/12/29",
                          "period_length": 12,
                          "audited": true,
                          "consolidated": true,
                          "revenue": 1852000000,
                          "cogs": -1310000000,
                          "gross_profit": 542000000,
                          "operating_profit": 470000000,
                          "net_profit": 398000000,
                          "eps": 1420,
                          "comparable_with_prev": true
                        },
                        {
                          "period_end": "1403/12/30",
                          "period_length": 12,
                          "audited": true,
                          "consolidated": true,
                          "revenue": 1490000000,
                          "cogs": -1090000000,
                          "gross_profit": 400000000,
                          "operating_profit": 341000000,
                          "net_profit": 296000000,
                          "eps": 1056,
                          "comparable_with_prev": false
                        }
                      ],
                      "basis": "discrete",
                      "has_quarterly": true
                    }
                  },
                  "quarterly": {
                    "summary": "سریِ فصلیِ تفاضلی",
                    "value": {
                      "symbol": "فولاد",
                      "unit": "million_rial",
                      "basis": "discrete",
                      "rows": [
                        {
                          "period_end": "1404/09/30",
                          "period_length": 3,
                          "audited": false,
                          "consolidated": false,
                          "fiscal_year_end": "1404/12/29",
                          "fiscal_year": 1404,
                          "quarter": 3,
                          "derived": true,
                          "mixed_basis": true,
                          "revenue": 879330176,
                          "cogs": -620000000,
                          "gross_profit": 259330176,
                          "operating_profit": 210000000,
                          "net_profit": 188930866,
                          "eps": 60,
                          "comparable_with_prev": true
                        }
                      ]
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "نماد مشخص نشده یا دوره نامعتبر است"
          }
        }
      }
    },
    "/api/fundamentals/screener": {
      "get": {
        "tags": [
          "نمادها"
        ],
        "summary": "غربالگرِ بنیادیِ مستقل",
        "description": "غربالگریِ بنیادی بر پایهٔ آخرین صورتِ مالیِ سالانهٔ کدال (codal_fundamentals) به‌همراهِ قیمتِ زنده — مستقل از اسکنرِ تکنیکالِ `/api/technical/scan`. هر ردیف: P/E (قیمتِ لحظه‌ای ÷ EPSِ سالانهٔ آخر، فقط اگر EPS مثبت باشد؛ وگرنه null)، حاشیهٔ خالص (`net_margin_pct`) و رشدِ درآمدِ سالانه نسبت به سالِ مالیِ قبل (`revenue_growth_pct`). **P/B و ROE عمداً نیستند**: هر دو به equity/ترازنامه نیاز دارند که فازِ ۱ِ استخراجِ کدال (فقط سودوزیان) پوشش نمی‌دهد.",
        "parameters": [
          {
            "name": "market",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "فیلترِ بازار (خالی = همه)"
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "default": 300,
              "maximum": 1000
            }
          }
        ],
        "responses": {
          "200": {
            "description": "آرایه‌ای از {symbol, name, market, price_last, period_end, eps, revenue, net_profit, pe, net_margin_pct, revenue_growth_pct}"
          }
        }
      }
    },
    "/api/codal/report-forecast": {
      "get": {
        "tags": [
          "نمادها"
        ],
        "summary": "پیش‌بینیِ تاریخِ گزارشِ فصلیِ بعدیِ کدال (برآوردی)",
        "description": "برآوردِ **محلی و غیررسمی** از تاریخِ انتشارِ گزارشِ فصلیِ بعدیِ یک ناشر، بر پایهٔ فاصلهٔ تاریخیِ دوره‌ها (~۹۱ روز) و میانگینِ تأخیرِ انتشارِ همان نماد نسبت به پایانِ دوره (از codal_statement_letters ↔ codal_announcements). کدال هیچ تقویمِ رسمیِ گزارش‌دهی منتشر نمی‌کند؛ این عدد اعلامِ رسمی نیست. با `symbol` برآوردِ همان نماد را می‌دهد؛ بدونِ آن، فهرستِ نمادهایی که برآوردِ انتشارشان تا `days` روزِ آینده است (نزدیک‌ترین اول) — ورودیِ بخشِ «گزارش‌های فصلیِ پیشِ‌رو»ی صفحهٔ تقویم.",
        "parameters": [
          {
            "name": "symbol",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "days",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "default": 14,
              "maximum": 90
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "default": 50,
              "maximum": 300
            }
          }
        ],
        "responses": {
          "200": {
            "description": "تک‌نمادی: {symbol, last_period_end, next_period_end, predicted_date, avg_lag_days, days_until, sample_letters}. چندنمادی: آرایه‌ای از همین شکل."
          }
        }
      }
    },
    "/api/shareholders/top-corporate": {
      "get": {
        "tags": [
          "نمادها"
        ],
        "summary": "برترین سهامدارانِ حقوقی (گزارشِ عمومی)",
        "description": "تجمیعِ سهام‌دارانِ عمدهٔ «حقوقیِ» بازار (تشخیص با هیوریستیکِ نامی) بر پایهٔ آخرین عکسِ shareholder_snapshots، مرتب بر مجموعِ ارزشِ بازاریِ سهامشان. نیازمندِ اجرای دوره‌ایِ `collector -backfill-shareholders`؛ در نبودِ آن آرایهٔ خالی می‌دهد.",
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "default": 50,
              "maximum": 500
            }
          }
        ],
        "responses": {
          "200": {
            "description": "آرایه‌ای از {holder_name, companies, total_value, total_percent_sum, as_of_d_even}"
          }
        }
      }
    },
    "/api/shareholders/history": {
      "get": {
        "tags": [
          "نمادها"
        ],
        "summary": "روندِ زمانیِ مالکیتِ یک سهام‌دار در یک نماد",
        "description": "ردیابیِ تغییرِ مالکیت در طولِ زمان: تعدادِ سهام و درصدِ مالکیتِ یک سهام‌دارِ مشخص در یک نماد، از عکس‌های روزانهٔ shareholder_snapshots، قدیم→جدید.",
        "parameters": [
          {
            "name": "symbol",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "holder",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "نامِ دقیقِ سهام‌دار، عیناً همان‌طور که در /api/shareholders همان نماد آمده"
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "default": 90,
              "maximum": 500
            }
          }
        ],
        "responses": {
          "200": {
            "description": "آرایه‌ای از {d_even, shares, percent}"
          },
          "400": {
            "description": "نماد یا نامِ سهام‌دار مشخص نشده"
          }
        }
      }
    },
    "/api/external/quotes": {
      "get": {
        "tags": [
          "بازارها"
        ],
        "summary": "قیمت بازارهای موازی — طلا، سکه و ارز",
        "description": "قیمت لحظه‌ای بازارهای موازی (دلار، یورو، سکه، طلای ۱۸ عیار، انس جهانی) از منبع بیرونی. `price` برای اقلام ریالی به ریال است و برای انس جهانی به دلار (فیلد `unit` تفکیک می‌کند). `source_ts` مهر زمان خودِ منبع است، نه زمان خواندنِ ما؛ تازگی باید بر همان سنجیده شود. در نبود داده آرایهٔ خالی برمی‌گردد، نه خطا.",
        "responses": {
          "200": {
            "description": "پاسخِ JSON",
            "content": {
              "application/json": {
                "example": [
                  {
                    "key": "usd",
                    "label": "دلار آمریکا",
                    "price": 1854000,
                    "change_pct": 0,
                    "unit": "rial",
                    "source": "tgju",
                    "source_ts": "2026-08-09T00:00:00+03:30"
                  },
                  {
                    "key": "gold_ounce",
                    "label": "انس جهانی طلا",
                    "price": 4373.04,
                    "change_pct": -0.36,
                    "unit": "usd",
                    "source": "tgju",
                    "source_ts": "2026-08-11T09:48:51+03:30"
                  }
                ]
              }
            }
          }
        }
      }
    },
    "/api/features/registry": {
      "get": {
        "tags": [
          "بازار"
        ],
        "summary": "رجیستریِ عمومیِ ویژگی‌هایِ مدل",
        "description": "فهرستِ ویژگی‌هایِ ورودیِ مدل‌ها (feature_registry) را می‌دهد؛ برایِ نمایش در صفحهٔ مدل‌ها. پیش‌تر این جدول فقط در گزارشِ داخلیِ ادمین خوانده می‌شد.",
        "responses": {
          "200": {
            "description": "پاسخِ JSON"
          }
        }
      }
    },
    "/api/factor-rank": {
      "get": {
        "tags": [
          "بازار"
        ],
        "summary": "مدلِ چندعاملیِ مقطعیِ رتبه‌بندیِ نمادها (مومنتوم/روند/نوسان/جریانِ پول)",
        "description": "مدلِ چندعاملیِ مقطعیِ رتبه‌بندیِ نمادها (مومنتوم/روند/نوسان/جریانِ پول).",
        "responses": {
          "200": {
            "description": "پاسخِ JSON",
            "content": {
              "application/json": {
                "example": [
                  {
                    "symbol": "زفجر",
                    "name": "كشاورزي و دامپروري فجر اصفهان",
                    "market": "فرابورس",
                    "close": 47450,
                    "composite": 2.087,
                    "mom20": 0.7805,
                    "mom60": 0.7161,
                    "z_mom20": 3.19,
                    "z_mom60": 1.77,
                    "z_trend": 2.77,
                    "z_lowvol": 1.76,
                    "z_flow": -0.29
                  },
                  {
                    "symbol": "نوين",
                    "name": "بيمه نوين",
                    "market": "فرابورس",
                    "close": 5950,
                    "composite": 2.06,
                    "mom20": 0.5986,
                    "mom60": 1.207,
                    "z_mom20": 2.23,
                    "z_mom60": 3.38,
                    "z_trend": 2.43,
                    "z_lowvol": 0.75,
                    "z_flow": -0.08
                  }
                ]
              }
            }
          }
        }
      }
    },
    "/api/filter": {
      "post": {
        "tags": [
          "ابزار"
        ],
        "summary": "فیلتر نمادها بر اساس شرط‌ها",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/FilterRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "نمادهای منطبق با شرط‌ها"
          }
        }
      }
    },
    "/api/fund-nav-history": {
      "get": {
        "tags": [
          "صندوق‌ها"
        ],
        "summary": "سری زمانیِ NAV یک صندوق (پارامتر symbol) برای نمودارِ روند",
        "description": "سری زمانیِ NAV یک صندوق (پارامتر symbol) برای نمودارِ روند.",
        "responses": {
          "200": {
            "description": "پاسخِ JSON"
          }
        }
      }
    },
    "/api/fund-returns": {
      "get": {
        "tags": [
          "صندوق‌ها"
        ],
        "summary": "بازدهِ دوره‌ایِ صندوق‌های قابل‌معامله (۱ه/۱م/۳م/۶م/۱س) برای مقایسه‌گر و رتبه‌بندیِ صندوق‌های…",
        "description": "بازدهِ دوره‌ایِ صندوق‌های قابل‌معامله (۱ه/۱م/۳م/۶م/۱س) برای مقایسه‌گر و رتبه‌بندیِ صندوق‌های هم‌رده. کش ۱۲۰ ثانیه (NAV به‌کندی تغییر می‌کند).",
        "responses": {
          "200": {
            "description": "پاسخِ JSON"
          }
        }
      }
    },
    "/api/funds": {
      "get": {
        "tags": [
          "صندوق‌ها"
        ],
        "summary": "فهرستِ صندوق‌های قابل‌معامله (ETF) با NAV، قیمت و حبابِ درصدی",
        "description": "فهرستِ صندوق‌های قابل‌معامله (ETF) با NAV، قیمت و حبابِ درصدی.",
        "responses": {
          "200": {
            "description": "پاسخِ JSON",
            "content": {
              "application/json": {
                "example": [
                  {
                    "symbol": "عيار",
                    "name": "صندوق طلاي عيار مفيد",
                    "sector_name": "صندوق سرمايه گذاري قابل معامله",
                    "nav": 488842,
                    "price_last": 501990,
                    "price_close": 499850,
                    "premium": 2.69,
                    "change_pct": 2.69,
                    "volume": 72189057,
                    "value": 36083667565274
                  },
                  {
                    "symbol": "ياقوت",
                    "name": "صندوق س ياقوت آگاه-ثابت",
                    "sector_name": "صندوق سرمايه گذاري قابل معامله",
                    "nav": 44183,
                    "price_last": 44232,
                    "price_close": 44232,
                    "premium": 0.11,
                    "change_pct": 0.13,
                    "volume": 653145526,
                    "value": 28889625468891
                  }
                ]
              }
            }
          }
        }
      }
    },
    "/api/home": {
      "get": {
        "tags": [
          "بازار"
        ],
        "summary": "داده‌های above-the-foldِ صفحهٔ اصلی را از کش/storeِ موجود تجمیع می‌کند (ZF-4002)",
        "description": "داده‌های above-the-foldِ صفحهٔ اصلی را از کش/storeِ موجود تجمیع می‌کند (ZF-4002). قواعد: هیچ فراخوانیِ خارجی در مسیرِ درخواست نیست؛ همان کلیدهای کشِ endpointهای موجود بازاستفاده می‌شوند (کارِ تکراری صفر)؛ هر بخش مستقل و partial است تا کندیِ یک بخش کلِ صفحه را بلاک نکند؛ endpointهای قدیمی دست‌نخورده می‌مانند.",
        "responses": {
          "200": {
            "description": "پاسخِ JSON"
          }
        }
      }
    },
    "/api/index-daily": {
      "get": {
        "tags": [
          "بازار"
        ],
        "summary": "تاریخچه‌ی روزانه‌ی شاخص را می‌دهد",
        "description": "تاریخچه‌ی روزانه‌ی شاخص را می‌دهد؛ kind=equal → هم‌وزن، kind=farabourse → شاخص کل فرابورس (آیفکس)، پیش‌فرض شاخص کل.",
        "responses": {
          "200": {
            "description": "پاسخِ JSON",
            "content": {
              "application/json": {
                "example": [
                  {
                    "d_even": 20260714,
                    "close": 4924683.6,
                    "high": 4924680,
                    "low": 4890740
                  },
                  {
                    "d_even": 20260713,
                    "close": 4966760.8,
                    "high": 4966760,
                    "low": 4940770
                  }
                ]
              }
            }
          }
        }
      }
    },
    "/api/indicators/history": {
      "get": {
        "tags": [
          "نشانگرها"
        ],
        "summary": "سریِ زمانیِ یک نشانگر",
        "description": "سریِ زمانیِ یک نشانگرِ مشخص برای یک نماد (صعودی بر اساسِ تاریخ).",
        "parameters": [
          {
            "name": "symbol",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "indicator",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 260
            }
          }
        ],
        "responses": {
          "200": {
            "description": "{data:[{dt,value,status}], meta}"
          },
          "400": {
            "description": "ورودی نامعتبر/نشانگرِ ناشناخته"
          },
          "404": {
            "description": "نماد یافت نشد"
          }
        }
      }
    },
    "/api/indicators/registry": {
      "get": {
        "tags": [
          "نشانگرها"
        ],
        "summary": "فهرستِ فرادادهٔ نشانگرها",
        "description": "فراداده‌ی همهٔ نشانگرهای پیاده‌سازی‌شده (id, kind, unit, period, inputs, label) + نسخهٔ فرمول. پایدار/آرام‌تغییر؛ ETag فعال.",
        "responses": {
          "200": {
            "description": "{data:[Spec], meta:{count, formula_version}}"
          }
        }
      }
    },
    "/api/indicators/symbol": {
      "get": {
        "tags": [
          "نشانگرها"
        ],
        "summary": "آخرین نشانگرهای یک نماد",
        "description": "آخرین مقادیرِ همهٔ نشانگرهای یک نماد در آخرین روزِ محاسبه‌شده، همراهِ وضعیتِ صریح (ready/insufficient_data/stale/calculation_error/unavailable/flat_series/zero_denominator/invalid_input).",
        "parameters": [
          {
            "name": "symbol",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "{data:[{indicator,value,status,unit,period,dt}], meta}"
          },
          "400": {
            "description": "نماد مشخص نشده"
          },
          "404": {
            "description": "نماد یافت نشد"
          }
        }
      }
    },
    "/api/insights": {
      "get": {
        "tags": [
          "بازار"
        ],
        "summary": "بینش‌های بازار",
        "description": "بینش‌های بازار را از اسنپ‌شاتِ زندهٔ overview محاسبه می‌کند (as-of یکسان، کش کوتاه).",
        "parameters": [
          {
            "name": "market",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "بازار (اختیاری)"
          }
        ],
        "responses": {
          "200": {
            "description": "پاسخِ JSON"
          }
        }
      }
    },
    "/api/instrument": {
      "get": {
        "tags": [
          "نمادها"
        ],
        "summary": "جزئیات کامل یک نماد",
        "parameters": [
          {
            "name": "symbol",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "آخرین وضعیت نماد شامل دفتر سفارش"
          }
        }
      }
    },
    "/api/instrument-boards": {
      "get": {
        "tags": [
          "نمادها"
        ],
        "summary": "تابلوهای معاملاتی یک نماد (عادی/خرده‌فروشی/فرعی/بلوکی)",
        "description": "TSETMC برای هر ابزار تا چهار insCode نگه می‌دارد و چهار رقمِ پایانیِ ISIN تابلو را مشخص می‌کند. این endpoint همه‌ی تابلوهای یک ابزار را با برچسب و توضیحِ نقشِ هرکدام برمی‌گرداند، به‌همراه جمعِ ارزش/حجم/تعدادِ معاملاتِ تابلوهایی که در آخرین روزِ معاملاتی فعال بوده‌اند. برای نمادهای بدونِ تابلوی فرعی (اختیار، حقِ تقدم، اوراق) فهرست خالی است.",
        "parameters": [
          {
            "name": "symbol",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "فهرست تابلوها + primary (نمادِ تابلوی عادی) + is_variant + totals"
          }
        }
      }
    },
    "/api/instruments": {
      "get": {
        "tags": [
          "بازار"
        ],
        "summary": "جزئیاتِ چندین نماد را در یک درخواست برمی‌گرداند",
        "description": "جزئیاتِ چندین نماد را در یک درخواست برمی‌گرداند. پارامتر: syms=A,B,C (کاما-جدا، حداکثر ۱۰۰)",
        "responses": {
          "200": {
            "description": "پاسخِ JSON"
          }
        }
      }
    },
    "/api/market-stats": {
      "get": {
        "tags": [
          "بازار"
        ],
        "summary": "آمار تجمیعیِ هر بازار (ارزش، پول حقیقی، تغییرِ وزنی، تعداد مثبت/منفی) را برای هابِ «بازارها» و…",
        "description": "آمار تجمیعیِ هر بازار (ارزش، پول حقیقی، تغییرِ وزنی، تعداد مثبت/منفی) را برای هابِ «بازارها» و نمای اختصاصیِ هر بازار برمی‌گرداند.",
        "responses": {
          "200": {
            "description": "پاسخِ JSON",
            "content": {
              "application/json": {
                "example": [
                  {
                    "market": "bourse",
                    "symbols": 597,
                    "positive": 257,
                    "negative": 327,
                    "change_pct": -0.25,
                    "net_real": -22956875560641,
                    "value": 213138585245812
                  },
                  {
                    "market": "farabourse",
                    "symbols": 544,
                    "positive": 266,
                    "negative": 266,
                    "change_pct": 0.35,
                    "net_real": -4185484926558,
                    "value": 56693086029047
                  },
                  {
                    "market": "sandogh",
                    "symbols": 476,
                    "positive": 228,
                    "negative": 239,
                    "change_pct": 0.21,
                    "net_real": 24050526850397,
                    "value": 488930635374606
                  }
                ]
              }
            }
          }
        }
      }
    },
    "/api/market/internals": {
      "get": {
        "tags": [
          "بازار"
        ],
        "summary": "سنجه‌های درونی کل بازار با مخرجِ صریح",
        "description": "پهنای واقعی بازار روی کلِ جهانِ نمادها (نه نمونه‌ی چند نماد): شمار نمادهای بالای میانگین ۲۰ و ۵۰ روزه، سقف/کف ۲۰ روزه، به‌همراه مخرجِ هر سنجه (چند نماد داده‌ی کافی داشته‌اند). نمادِ بدون داده هرگز صفر گزارش نمی‌شود؛ اگر مخرج صفر باشد یعنی داده در دسترس نیست.",
        "parameters": [
          {
            "name": "market",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "محدودکردن به یک بازار (مثلاً bourse یا farabourse). خالی یعنی کل بازار."
          }
        ],
        "responses": {
          "200": {
            "description": "پاسخِ JSON",
            "content": {
              "application/json": {
                "example": {
                  "universe": 1301,
                  "ma20_universe": 1284,
                  "above_ma20": 785,
                  "ma50_universe": 1094,
                  "above_ma50": 790,
                  "new_high_20": 96,
                  "new_low_20": 41,
                  "as_of": 20260727
                }
              }
            }
          }
        }
      }
    },
    "/api/market/volume": {
      "get": {
        "tags": [
          "بازار"
        ],
        "summary": "تاریخچه‌ی گردشِ معاملاتِ کل بازار در بازه‌های روز/هفته/ماه",
        "description": "جمعِ ارزشِ ریالی (`value`)، تعدادِ سهمِ معامله‌شده (`volume`) و شمارِ معاملات (`trades`) روی کلِ جهانِ نمادها، قدیمی→جدید. `bucket=day|week|month`؛ هفته به شنبه لنگر می‌خورد (هفته‌ی معاملاتیِ ایران) و ماه، ماهِ شمسی است. `d_even` شروعِ تقویمیِ سطل و `end` آخرین روزِ معاملاتیِ داخلِ آن است. سطلی که هنوز کامل نشده — یا روزی که هنوز بک‌فیل نشده — پرچمِ `provisional: true` می‌گیرد و نباید به‌عنوان افتِ واقعی خوانده شود. جهانِ نمادها همان جهانِ صفحه‌ی نخست است (بدونِ تابلوهای فرعی و اختیارها، و در نمای «همه» بدونِ «اوراق و سایر»)، پس عدد با تیترِ venue‌محورِ TSETMC برابر نیست.",
        "parameters": [
          {
            "name": "market",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "محدودکردن به یک بازار (bourse|farabourse|tala|daramad|sandogh|oraagh). خالی یعنی کل بازار."
          },
          {
            "name": "bucket",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "day",
                "week",
                "month"
              ],
              "default": "day"
            },
            "description": "بازه‌ی تجمیع. مقدارِ نامعتبر به day نگاشت می‌شود."
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer"
            },
            "description": "تعدادِ سطلِ خروجی (نه تعدادِ روز). پیش‌فرض/سقف: day ۹۰/۴۰۰، week ۵۲/۶۰، month ۲۴/۲۴. سقف‌ها اندازه‌گیری‌شده‌اند: فراتر از آن planner به Seq Scanِ کلِ daily_history می‌افتد و درخواست از مهلت عبور می‌کند."
          }
        ],
        "responses": {
          "200": {
            "description": "پاسخِ JSON",
            "content": {
              "application/json": {
                "example": [
                  {
                    "d_even": 20260808,
                    "end": 20260808,
                    "value": 167210000000000000,
                    "volume": 12840000000,
                    "trades": 1310000,
                    "days": 1,
                    "symbols": 1035
                  },
                  {
                    "d_even": 20260809,
                    "end": 20260809,
                    "value": 41200000000000000,
                    "volume": 3900000000,
                    "trades": 410000,
                    "days": 1,
                    "symbols": 207,
                    "provisional": true
                  }
                ]
              }
            }
          }
        }
      }
    },
    "/api/markets": {
      "get": {
        "tags": [
          "بازار"
        ],
        "summary": "شمار نمادهای هر دسته بازار",
        "description": "تعداد نمادهای فعال هر دسته: bourse، farabourse، tala، sandogh، oraagh و all.",
        "responses": {
          "200": {
            "description": "نگاشت دسته→تعداد",
            "content": {
              "application/json": {
                "example": {
                  "all": 2329,
                  "bourse": 597,
                  "farabourse": 544,
                  "oraagh": 699,
                  "sandogh": 476,
                  "tala": 13
                }
              }
            }
          }
        }
      }
    },
    "/api/moneyflow": {
      "get": {
        "tags": [
          "جریان پول"
        ],
        "summary": "آخرین جریان پول کل بازار",
        "responses": {
          "200": {
            "description": "خالص ورود/خروج پول حقیقی",
            "content": {
              "application/json": {
                "example": {
                  "legal_buy_value": 2621227117902102,
                  "legal_sell_value": 2619397941339474,
                  "net_real": -1829176562628,
                  "real_buy_value": 331245814662918,
                  "real_sell_value": 333074991225546,
                  "ts": "2026-07-14T16:59:52+03:30"
                }
              }
            }
          }
        }
      }
    },
    "/api/moneyflow/history": {
      "get": {
        "tags": [
          "جریان پول"
        ],
        "summary": "روند جریان پول نشست",
        "responses": {
          "200": {
            "description": "آرایه‌ای از نقاط جریان پول"
          }
        },
        "parameters": [
          {
            "name": "date",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "example": "2026-07-28"
            },
            "description": "تونل زمان: نشستِ موردنظر به‌صورتِ YYYY-MM-DD یا YYYYMMDD. بدونِ این پارامتر، نشستِ جاری برگردانده می‌شود."
          }
        ],
        "description": "با date، فقط نمای «همه بازارها» برگردانده می‌شود و پارامتر market نادیده گرفته می‌شود."
      }
    },
    "/api/movers": {
      "get": {
        "tags": [
          "بازار"
        ],
        "summary": "برترین نمادها",
        "parameters": [
          {
            "name": "kind",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "value",
                "gainers",
                "losers"
              ]
            },
            "description": "معیار: بیشترین ارزش، بیشترین رشد، بیشترین افت"
          },
          {
            "name": "market",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "",
                "bourse",
                "farabourse",
                "tala",
                "sandogh",
                "oraagh"
              ]
            },
            "description": "تفکیک بازار (خالی = همه)"
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 12
            }
          }
        ],
        "responses": {
          "200": {
            "description": "آرایه‌ای از نمادها",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/Mover"
                  }
                },
                "example": [
                  {
                    "symbol": "عيار",
                    "name": "صندوق طلاي عيار مفيد",
                    "market": "صندوق طلا",
                    "price_last": 501990,
                    "change_pct": 2.69,
                    "volume": 72189057,
                    "value": 36083667565274
                  },
                  {
                    "symbol": "ياقوت",
                    "name": "صندوق س ياقوت آگاه-ثابت",
                    "market": "صندوق",
                    "price_last": 44232,
                    "change_pct": 0.13,
                    "volume": 653145526,
                    "value": 28889625468891
                  }
                ]
              }
            }
          }
        }
      }
    },
    "/api/option-chain": {
      "get": {
        "tags": [
          "بازار"
        ],
        "summary": "زنجیره‌ی اختیار (نامِ کاملِ حاویِ قیمتِ اعمال/سررسید + مظنه‌ها) و نگاشتِ قیمتِ داراییِ پایه را…",
        "description": "زنجیره‌ی اختیار (نامِ کاملِ حاویِ قیمتِ اعمال/سررسید + مظنه‌ها) و نگاشتِ قیمتِ داراییِ پایه را می‌دهد تا کلاینت نوسانِ ضمنی و سطحِ IV را بسازد.",
        "responses": {
          "200": {
            "description": "پاسخِ JSON"
          }
        }
      }
    },
    "/api/options": {
      "get": {
        "tags": [
          "بازار"
        ],
        "summary": "دیده‌بانِ اختیار معامله: فهرستِ قراردادهای اختیار + خلاصه‌ی کل",
        "description": "دیده‌بانِ اختیار معامله: فهرستِ قراردادهای اختیار + خلاصه‌ی کل. side=call (اختیار خرید) | put (اختیار فروش) | \"\" (همه).",
        "responses": {
          "200": {
            "description": "پاسخِ JSON"
          }
        }
      }
    },
    "/api/options/chain": {
      "get": {
        "tags": [
          "بازار"
        ],
        "summary": "زنجیره‌ی اختیارِ یک نمادِ پایه را برمی‌گرداند (کش ۲ دقیقه)",
        "description": "زنجیره‌ی اختیارِ یک نمادِ پایه را برمی‌گرداند (کش ۲ دقیقه). قیمتِ بازار از latest است؛ IV/Greeks و نرخِ بدونِ ریسک مقادیرِ مشتق از مدلِ Black-Scholes (اروپایی) هستند و در پاسخ صراحتاً برچسب می‌خورند. بدونِ معامله‌ی واقعی.",
        "responses": {
          "200": {
            "description": "پاسخِ JSON"
          }
        }
      }
    },
    "/api/orderbook": {
      "get": {
        "tags": [
          "بازار"
        ],
        "summary": "دفترچه‌ی سفارشِ ۵ ردیفه‌ی یک نماد را برای نمودارِ عمق و تحلیلِ صف می‌دهد",
        "description": "دفترچه‌ی سفارشِ ۵ ردیفه‌ی یک نماد را برای نمودارِ عمق و تحلیلِ صف می‌دهد.",
        "responses": {
          "200": {
            "description": "پاسخِ JSON"
          }
        }
      }
    },
    "/api/overview": {
      "get": {
        "tags": [
          "بازار"
        ],
        "summary": "آخرین نمای کلی بازار",
        "description": "شاخص کل، شاخص هم‌وزن، ارزش و حجم کل و وضعیت بازار.",
        "responses": {
          "200": {
            "description": "موفق",
            "content": {
              "application/json": {
                "example": {
                  "date": 20260714,
                  "time": 155702,
                  "value": 326058052461938,
                  "volume": 63697810745,
                  "indexLast": 4924683.63,
                  "tradeCount": 645489,
                  "indexChange": -42077.13,
                  "marketState": "صندوق‌های طلا باز",
                  "marketValue": 144178074999375780,
                  "indexEqualLast": 1323516.72,
                  "indexEqualChange": -3165.36
                }
              }
            }
          }
        }
      }
    },
    "/api/overview/history": {
      "get": {
        "tags": [
          "بازار"
        ],
        "summary": "روند شاخص نشست اخیر",
        "description": "نقاط درون‌روزِ آخرین نشست معاملاتی (شاخص کل/هم‌وزن و ارزش).",
        "responses": {
          "200": {
            "description": "آرایه‌ای از نقاط زمانی"
          }
        },
        "parameters": [
          {
            "name": "date",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "example": "2026-07-28"
            },
            "description": "تونل زمان: نشستِ موردنظر به‌صورتِ YYYY-MM-DD یا YYYYMMDD. بدونِ این پارامتر، نشستِ جاری برگردانده می‌شود."
          }
        ]
      }
    },
    "/api/pairs": {
      "get": {
        "tags": [
          "بازار"
        ],
        "summary": "نمادهای جامانده نسبت به رهبرِ صنعت (z-scoreِ نسبتِ قیمت) — کاندیدای هم‌گرایی",
        "description": "نمادهای جامانده نسبت به رهبرِ صنعت (z-scoreِ نسبتِ قیمت) — کاندیدای هم‌گرایی.",
        "responses": {
          "200": {
            "description": "پاسخِ JSON",
            "content": {
              "application/json": {
                "example": [
                  {
                    "symbol": "زمگسا",
                    "name": "كشاورزي‌ ودامپروي‌ مگسال‌",
                    "sector": "زراعت و خدمات وابسته",
                    "leader": "زفجر",
                    "spread_pct": -22.25,
                    "z": -3.68,
                    "n": 68
                  },
                  {
                    "symbol": "سفارود",
                    "name": "كارخانه فارسيت درود",
                    "sector": "ساير محصولات كاني غيرفلزي",
                    "leader": "كسرا",
                    "spread_pct": -37.28,
                    "z": -3.26,
                    "n": 69
                  }
                ]
              }
            }
          }
        }
      }
    },
    "/api/pairs/series": {
      "get": {
        "tags": [
          "بازار"
        ],
        "summary": "سریِ تاریخیِ Spread/Z-Score یک جفت",
        "description": "سریِ روزانهٔ نسبتِ قیمت به رهبرِ صنعت، انحرافِ درصدی و z-score را برای یک نماد می‌دهد (نمودارِ زندهٔ صفحهٔ pairs).",
        "parameters": [
          {
            "name": "symbol",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "نمادِ عضوِ صنعت"
          },
          {
            "name": "days",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer"
            },
            "description": "طولِ پنجره (پیش‌فرض ۱۲۰، حداکثر ۲۶۰)"
          }
        ],
        "responses": {
          "200": {
            "description": "پاسخِ JSON"
          },
          "400": {
            "description": "نمادِ نامعتبر"
          }
        }
      }
    },
    "/api/pairs/backtest": {
      "get": {
        "tags": [
          "بازار"
        ],
        "summary": "بک‌تستِ تاریخیِ یک جفتِ معاملاتی — ورود/خروج بر پایه‌ی z-scoreِ غلتان",
        "description": "با آستانه‌های ورود/خروجِ قابل‌تنظیم روی سریِ روزانه‌ی نسبتِ symbol به leader شبیه‌سازی می‌شود؛ نیمه‌عمرِ بازگشت‌به‌میانگین هم برمی‌گردد.",
        "parameters": [
          {
            "name": "symbol",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "نمادِ عضوِ جفت"
          },
          {
            "name": "leader",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "نمادِ رهبرِ صنعت (از پاسخِ /api/pairs)"
          },
          {
            "name": "entry",
            "in": "query",
            "required": false,
            "schema": {
              "type": "number",
              "example": 2
            },
            "description": "آستانه‌ی |z| ورود (پیش‌فرض ۲، بازه ۰٫۵ تا ۴)"
          },
          {
            "name": "exit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "number",
              "example": 0.5
            },
            "description": "آستانه‌ی |z| خروج (پیش‌فرض ۰٫۵، باید کمتر از entry باشد)"
          },
          {
            "name": "window",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "example": 120
            },
            "description": "پنجره‌ی محاسبه‌ی z-scoreِ غلتان به روز (پیش‌فرض ۱۲۰)"
          }
        ],
        "responses": {
          "200": {
            "description": "پاسخِ JSON: trades, win_rate, avg_return_pct, max_drawdown_pct, half_life, equity_curve"
          }
        }
      }
    },
    "/api/paper/account": {
      "get": {
        "tags": [
          "معاملهٔ کاغذی"
        ],
        "summary": "دریافتِ حسابِ معاملهٔ کاغذی",
        "description": "یک حسابِ معاملهٔ کاغذیِ کاربر را با شناسه برمی‌گرداند.",
        "parameters": [
          {
            "name": "id",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "شناسهٔ حساب"
          }
        ],
        "responses": {
          "200": {
            "description": "پاسخِ JSON"
          },
          "401": {
            "description": "نیازمندِ احراز هویت"
          }
        }
      }
    },
    "/api/paper/accounts": {
      "get": {
        "tags": [
          "معاملهٔ کاغذی"
        ],
        "summary": "فهرستِ حساب‌های معاملهٔ کاغذی",
        "description": "حساب‌های معاملهٔ کاغذیِ کاربرِ احرازشده را فهرست می‌کند.",
        "responses": {
          "200": {
            "description": "پاسخِ JSON"
          },
          "401": {
            "description": "نیازمندِ احراز هویت"
          }
        }
      }
    },
    "/api/paper/accounts/create": {
      "post": {
        "tags": [
          "معاملهٔ کاغذی"
        ],
        "summary": "ساختِ حسابِ معاملهٔ کاغذی",
        "description": "حسابِ معاملهٔ کاغذیِ جدید با نام و موجودیِ اولیه می‌سازد.",
        "responses": {
          "200": {
            "description": "پاسخِ JSON"
          },
          "401": {
            "description": "نیازمندِ احراز هویت"
          }
        }
      }
    },
    "/api/paper/automation": {
      "get": {
        "tags": [
          "معاملهٔ کاغذی"
        ],
        "summary": "اتصالِ استراتژی به حسابِ کاغذی",
        "description": "GET فهرستِ اتصال‌ها؛ POST اتصالِ یک استراتژی به حساب با درصدِ تخصیص/سقفِ موقعیت.",
        "responses": {
          "200": {
            "description": "پاسخِ JSON"
          },
          "401": {
            "description": "نیازمندِ احراز هویت"
          }
        }
      },
      "post": {
        "tags": [
          "معاملهٔ کاغذی"
        ],
        "summary": "اتصالِ استراتژی به حسابِ کاغذی",
        "description": "GET فهرستِ اتصال‌ها؛ POST اتصالِ یک استراتژی به حساب با درصدِ تخصیص/سقفِ موقعیت.",
        "responses": {
          "200": {
            "description": "پاسخِ JSON"
          },
          "401": {
            "description": "نیازمندِ احراز هویت"
          }
        }
      }
    },
    "/api/paper/automation/run": {
      "post": {
        "tags": [
          "معاملهٔ کاغذی"
        ],
        "summary": "تغییرِ وضعیتِ اتصالِ اتوماسیون",
        "description": "وضعیتِ یک اتصالِ استراتژی-به-حساب را تغییر می‌دهد.",
        "responses": {
          "200": {
            "description": "پاسخِ JSON"
          },
          "401": {
            "description": "نیازمندِ احراز هویت"
          }
        }
      }
    },
    "/api/paper/trade-notes": {
      "get": {
        "tags": [
          "معاملهٔ کاغذی"
        ],
        "summary": "دفترچهٔ یادداشتِ معاملاتی",
        "description": "یادداشت‌های معاملاتیِ کاربر (دلیلِ ورود/خروج) را فهرست می‌کند؛ با پارامترِ symbol فیلتر می‌شود.",
        "responses": {
          "200": {
            "description": "پاسخِ JSON"
          },
          "401": {
            "description": "نیازمندِ احراز هویت"
          }
        }
      }
    },
    "/api/paper/trade-notes/save": {
      "post": {
        "tags": [
          "معاملهٔ کاغذی"
        ],
        "summary": "ذخیرهٔ یادداشتِ معاملاتی",
        "description": "یک یادداشتِ معاملاتی می‌سازد یا ویرایش می‌کند.",
        "responses": {
          "200": {
            "description": "پاسخِ JSON"
          },
          "401": {
            "description": "نیازمندِ احراز هویت"
          }
        }
      }
    },
    "/api/paper/trade-notes/delete": {
      "post": {
        "tags": [
          "معاملهٔ کاغذی"
        ],
        "summary": "حذفِ یادداشتِ معاملاتی",
        "description": "یک یادداشتِ معاملاتی را حذف می‌کند.",
        "responses": {
          "200": {
            "description": "پاسخِ JSON"
          },
          "401": {
            "description": "نیازمندِ احراز هویت"
          }
        }
      }
    },
    "/api/paper/execution-history": {
      "get": {
        "tags": [
          "معاملهٔ کاغذی"
        ],
        "summary": "تاریخچهٔ اجرایِ معاملهٔ کاغذی",
        "description": "تاریخچهٔ کاملِ تصمیماتِ معاملهٔ کاغذیِ کاربر (سفارش/رد ریسک/کلیدِ توقف) را از execution_audit_log می‌دهد.",
        "responses": {
          "200": {
            "description": "پاسخِ JSON"
          },
          "401": {
            "description": "نیازمندِ احراز هویت"
          }
        }
      }
    },
    "/api/paper/kill-switch": {
      "post": {
        "tags": [
          "معاملهٔ کاغذی"
        ],
        "summary": "توقفِ اضطراریِ حسابِ کاغذی",
        "description": "توقفِ اضطراری را برای یک حسابِ معاملهٔ کاغذی روشن/خاموش می‌کند.",
        "responses": {
          "200": {
            "description": "پاسخِ JSON"
          },
          "401": {
            "description": "نیازمندِ احراز هویت"
          }
        }
      }
    },
    "/api/paper/order": {
      "post": {
        "tags": [
          "معاملهٔ کاغذی"
        ],
        "summary": "ثبتِ سفارشِ شبیه‌سازی‌شده",
        "description": "سفارشِ خرید/فروشِ کاغذی ثبت می‌کند. تنها شبیه‌سازی؛ هیچ سفارشِ واقعیِ کارگزاری ارسال نمی‌شود (simulated_only_no_real_orders، §1.4).",
        "responses": {
          "200": {
            "description": "پاسخِ JSON"
          },
          "401": {
            "description": "نیازمندِ احراز هویت"
          }
        }
      }
    },
    "/api/paper/performance": {
      "get": {
        "tags": [
          "معاملهٔ کاغذی"
        ],
        "summary": "گزارشِ عملکردِ حسابِ کاغذی",
        "description": "گزارشِ عملکرد/بازدهِ حسابِ معاملهٔ کاغذی را با شناسه می‌دهد.",
        "parameters": [
          {
            "name": "id",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "شناسهٔ حساب"
          }
        ],
        "responses": {
          "200": {
            "description": "پاسخِ JSON"
          },
          "401": {
            "description": "نیازمندِ احراز هویت"
          }
        }
      }
    },
    "/api/paper/reconciliation": {
      "get": {
        "tags": [
          "معاملهٔ کاغذی"
        ],
        "summary": "مغایرت‌گیریِ حسابِ کاغذی",
        "description": "گزارشِ مغایرت‌گیریِ موجودی/موقعیت‌های حسابِ کاغذی را می‌دهد.",
        "parameters": [
          {
            "name": "id",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "شناسهٔ حساب"
          }
        ],
        "responses": {
          "200": {
            "description": "پاسخِ JSON"
          },
          "401": {
            "description": "نیازمندِ احراز هویت"
          }
        }
      }
    },
    "/api/portfolio-build": {
      "get": {
        "tags": [
          "بازار"
        ],
        "summary": "وزنِ برابری‌ریسکِ ساده (معکوسِ نوسان) برای فهرستِ نمادهای دیده‌بان",
        "description": "وزنِ برابری‌ریسکِ ساده (معکوسِ نوسان) برای فهرستِ نمادهای دیده‌بان.",
        "responses": {
          "200": {
            "description": "پاسخِ JSON"
          }
        }
      }
    },
    "/api/premarket": {
      "get": {
        "tags": [
          "بازار"
        ],
        "summary": "دیده‌بانِ پیش‌گشایش (#12): قیمتِ نظریِ بازگشایی و توازنِ صفِ خرید/فروش را از دفترچه‌ی سفارشِ…",
        "description": "دیده‌بانِ پیش‌گشایش (#12): قیمتِ نظریِ بازگشایی و توازنِ صفِ خرید/فروش را از دفترچه‌ی سفارشِ جاری محاسبه می‌کند و بر پایه‌ی درصدِ تغییر نسبت به دیروز مرتب می‌کند.",
        "responses": {
          "200": {
            "description": "پاسخِ JSON",
            "content": {
              "application/json": {
                "example": {
                  "count": 1828,
                  "preOpen": false,
                  "rows": [
                    {
                      "symbol": "عقرن",
                      "name": "سلف موازي پديده شيمي قرن",
                      "ref": 1332684,
                      "indicative": 1779793,
                      "bestBid": 1763046,
                      "bestAsk": 1796540,
                      "buyQ": 20000,
                      "sellQ": 20000,
                      "gapPct": 33.55,
                      "imbalance": 0.5,
                      "status": "normal"
                    },
                    {
                      "symbol": "ونوين4",
                      "name": "بانك‌اقتصادنوين‌",
                      "ref": 3710,
                      "indicative": 4822,
                      "bestBid": 0,
                      "bestAsk": 4822,
                      "buyQ": 0,
                      "sellQ": 24314511900,
                      "gapPct": 20.93,
                      "imbalance": 0,
                      "status": "sell_queue"
                    }
                  ]
                }
              }
            }
          }
        }
      }
    },
    "/api/push/subscribe": {
      "post": {
        "tags": [
          "اعلان"
        ],
        "summary": "اشتراک Push مرورگر را ثبت می‌کند",
        "description": "اشتراک Push مرورگر را ثبت می‌کند.",
        "responses": {
          "200": {
            "description": "پاسخِ JSON"
          }
        }
      }
    },
    "/api/push/unsubscribe": {
      "post": {
        "tags": [
          "اعلان"
        ],
        "summary": "یک اشتراک Push را حذف می‌کند",
        "description": "یک اشتراک Push را حذف می‌کند.",
        "responses": {
          "200": {
            "description": "پاسخِ JSON"
          }
        }
      }
    },
    "/api/push/vapid": {
      "get": {
        "tags": [
          "اعلان"
        ],
        "summary": "کلید عمومی VAPID را برای اشتراک مرورگر برمی‌گرداند",
        "description": "کلید عمومی VAPID را برای اشتراک مرورگر برمی‌گرداند.",
        "responses": {
          "200": {
            "description": "پاسخِ JSON"
          }
        }
      }
    },
    "/api/quality": {
      "get": {
        "tags": [
          "بازار"
        ],
        "summary": "آخرین رخدادهای کیفیتِ داده را می‌دهد (پوشش/کهنگی/اعتبارِ داده)",
        "description": "آخرین رخدادهای کیفیتِ داده را می‌دهد (پوشش/کهنگی/اعتبارِ داده). قالبِ استاندارد: {data, meta}. کشِ کوتاه چون داده هر ساعت به‌روز می‌شود.",
        "responses": {
          "200": {
            "description": "پاسخِ JSON"
          }
        }
      }
    },
    "/api/quality/trend": {
      "get": {
        "tags": [
          "بازار"
        ],
        "summary": "روندِ روزانهٔ کیفیتِ داده",
        "description": "تعدادِ رخدادهای data_quality_log را روزبه‌روز (به وقتِ تهران) تجمیع می‌کند تا بدترشدنِ تدریجیِ منابع دیده شود. روزهای بدونِ رخداد عمداً ردیف ندارند؛ صفرِ ساختگی «سلامتِ کامل» را برای روزی که کالکتور اصلاً اجرا نشده جعل می‌کند. قالبِ استاندارد: {data, meta}.",
        "parameters": [
          {
            "name": "days",
            "in": "query",
            "required": false,
            "description": "بازهٔ روند به روز (پیش‌فرض ۳۰، سقف ۱۸۰)",
            "schema": {
              "type": "integer",
              "default": 30,
              "minimum": 1,
              "maximum": 180
            }
          }
        ],
        "responses": {
          "200": {
            "description": "پاسخِ JSON",
            "content": {
              "application/json": {
                "example": {
                  "data": [
                    {
                      "d": "2026-07-30",
                      "total": 214,
                      "errors": 0,
                      "warns": 12,
                      "infos": 202,
                      "sources": 9,
                      "bad_sources": 2
                    }
                  ],
                  "meta": {
                    "days": 30,
                    "count": 21
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/realmoney": {
      "get": {
        "tags": [
          "جریان پول"
        ],
        "summary": "برترین ورود/خروج پول حقیقی",
        "parameters": [
          {
            "name": "kind",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "in",
                "out"
              ]
            }
          },
          {
            "name": "market",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 12
            }
          }
        ],
        "responses": {
          "200": {
            "description": "آرایه‌ای از نمادها بر اساس خالص پول حقیقی",
            "content": {
              "application/json": {
                "example": [
                  {
                    "symbol": "پاسارگاد",
                    "name": "صندوق س.درآمد ثابت پاسارگاد-د",
                    "market": "صندوق",
                    "price_last": 16279,
                    "net_real": 3342411143730,
                    "buy_power": 1.18
                  },
                  {
                    "symbol": "آوند",
                    "name": "صندوق س. آوند مفيد-د",
                    "market": "صندوق",
                    "price_last": 28524,
                    "net_real": 3204794794824,
                    "buy_power": 1.35
                  }
                ]
              }
            }
          }
        }
      }
    },
    "/api/recommend": {
      "get": {
        "tags": [
          "استراتژی و بک‌تست"
        ],
        "summary": "رتبه‌بندیِ نمادها بر پایه‌ی امتیازِ ترکیبیِ تکنیکال/پرایس‌اکشن",
        "description": "رتبه‌بندیِ نمادها بر پایه‌ی امتیازِ ترکیبیِ تکنیکال/پرایس‌اکشن. محاسبه‌ی سنگین (اسکنِ daily_history) با کش پشتِ درخواست سرد نمی‌ماند.",
        "responses": {
          "200": {
            "description": "پاسخِ JSON"
          }
        }
      }
    },
    "/api/regime": {
      "get": {
        "tags": [
          "بازار"
        ],
        "summary": "رژیمِ بازار (صعودی/نزولی/خنثی) از شاخصِ کل و پهنای بازار + گرایشِ استراتژی",
        "description": "رژیمِ بازار (صعودی/نزولی/خنثی) از شاخصِ کل و پهنای بازار + گرایشِ استراتژی.",
        "responses": {
          "200": {
            "description": "پاسخِ JSON",
            "content": {
              "application/json": {
                "example": {
                  "index_close": 4924684,
                  "label": "روندِ صعودیِ پابرجا (بالای میانگین‌های ۵۰ و ۲۰۰)",
                  "ma200": 3761742,
                  "ma50": 4414827,
                  "mom20": 7.22,
                  "negative": 963,
                  "positive": 879,
                  "regime": "صعودی",
                  "tilt": "استراتژی‌های روندی و شکست (مومنتوم، تقاطعِ میانگین، دانچیان)؛ خریدِ اصلاح."
                }
              }
            }
          }
        }
      }
    },
    "/api/scan-filters/shared": {
      "get": {
        "tags": [
          "بازار"
        ],
        "summary": "filters/shared?token=… — عمومی",
        "description": "filters/shared?token=…  — عمومی. لینکِ خصوصی فقط برای مالکِ واردشده باز می‌شود؛ در غیرِ این صورت 404 (بدونِ لو دادنِ وجود).",
        "parameters": [
          {
            "name": "token",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "توکنِ اشتراکِ عمومی"
          }
        ],
        "responses": {
          "200": {
            "description": "پاسخِ JSON"
          }
        }
      }
    },
    "/api/search": {
      "get": {
        "tags": [
          "نمادها"
        ],
        "summary": "جست‌وجوی نماد",
        "parameters": [
          {
            "name": "q",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "بخشی از نام یا نماد"
          }
        ],
        "responses": {
          "200": {
            "description": "نتایج جست‌وجو",
            "content": {
              "application/json": {
                "example": [
                  {
                    "symbol": "فسبزوار",
                    "name": "پارس فولاد سبزوار",
                    "ins_code": "37284308569715577",
                    "market": "بورس",
                    "price_last": 55360,
                    "change_pct": 0.31,
                    "value": 538285277660
                  },
                  {
                    "symbol": "هرمز",
                    "name": "فولاد هرمزگان جنوب",
                    "ins_code": "70498485598181604",
                    "market": "فرابورس",
                    "price_last": 3060,
                    "change_pct": -1.29,
                    "value": 3396000000
                  }
                ]
              }
            }
          }
        }
      }
    },
    "/api/sector-peers": {
      "get": {
        "tags": [
          "بازار"
        ],
        "summary": "نسبت‌های بنیادیِ نماد (P/E, P/S) کنارِ میانگینِ صنعت + هم‌گروهی‌ها",
        "description": "نسبت‌های بنیادیِ نماد (P/E, P/S) کنارِ میانگینِ صنعت + هم‌گروهی‌ها.",
        "responses": {
          "200": {
            "description": "پاسخِ JSON"
          }
        }
      }
    },
    "/api/sector-symbols": {
      "get": {
        "tags": [
          "بازار"
        ],
        "summary": "نمادهای یک صنعت",
        "description": "نمادهای یک صنعت را بر پایهٔ کد یا نامِ صنعت (با بازار و محدودیتِ تعداد) برمی‌گرداند.",
        "parameters": [
          {
            "name": "code",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "کدِ صنعت"
          },
          {
            "name": "name",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "نامِ صنعت"
          },
          {
            "name": "market",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "بازار"
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "حداکثر تعداد نماد"
          }
        ],
        "responses": {
          "200": {
            "description": "پاسخِ JSON"
          }
        }
      }
    },
    "/api/sectors": {
      "get": {
        "tags": [
          "بازار"
        ],
        "summary": "نقشهٔ صنایع (Treemap)",
        "description": "نقشهٔ درختیِ صنایع بازار به‌همراهِ ارزشِ بازارِ هر صنعت را می‌دهد.",
        "parameters": [
          {
            "name": "market",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "بازار (اختیاری)"
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "حداکثر تعداد صنایع"
          }
        ],
        "responses": {
          "200": {
            "description": "پاسخِ JSON",
            "content": {
              "application/json": {
                "example": [
                  {
                    "sector_code": "57",
                    "sector_name": "بانكها و موسسات اعتباري",
                    "symbols": 30,
                    "positive": 6,
                    "negative": 23,
                    "change_pct": -1.78,
                    "buy_power": 0.9,
                    "net_real": -11668478379560,
                    "sector_pe": 11.24,
                    "value": 38972658375056
                  },
                  {
                    "sector_code": "23",
                    "sector_name": "فراورده هاي نفتي، كك و سوخت هسته اي",
                    "symbols": 20,
                    "positive": 6,
                    "negative": 14,
                    "change_pct": -2.47,
                    "buy_power": 0.55,
                    "net_real": -8530638599180,
                    "sector_pe": 6.29,
                    "value": 23854428479730
                  }
                ]
              }
            }
          }
        }
      }
    },
    "/api/sessions": {
      "get": {
        "tags": [
          "بازار"
        ],
        "summary": "فهرست نشست‌های معاملاتی موجود",
        "description": "ورودیِ «تونل زمان»: نشست‌هایی که سریِ زمانیِ درون‌روز برایشان ذخیره شده است. breadth_points صفر یعنی نمودارِ پهنای بازار برای آن نشست داده ندارد.",
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "default": 60,
              "maximum": 180
            }
          }
        ],
        "responses": {
          "200": {
            "description": "آرایه‌ای از نشست‌ها به ترتیبِ نزولیِ تاریخ",
            "content": {
              "application/json": {
                "example": [
                  {
                    "date": 20260729,
                    "start_ts": "2026-07-29T09:01:11.328414+03:30",
                    "end_ts": "2026-07-29T16:59:57.965959+03:30",
                    "points": 4907,
                    "breadth_points": 4919
                  }
                ]
              }
            }
          }
        }
      }
    },
    "/api/share/portfolio": {
      "post": {
        "tags": [
          "بازار"
        ],
        "summary": "عمومی، بدونِ احراز هویت",
        "description": "عمومی، بدونِ احراز هویت.",
        "parameters": [
          {
            "name": "t",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "توکنِ اشتراکِ عمومی"
          }
        ],
        "responses": {
          "200": {
            "description": "پاسخِ JSON"
          }
        }
      }
    },
    "/api/shareholders": {
      "get": {
        "tags": [
          "نمادها"
        ],
        "summary": "سهام‌داران عمده‌ی نماد",
        "description": "فهرستِ سهام‌دارانِ عمده‌ی فعلیِ یک نماد (زنده از سرویس GetInstrumentShareHolderLast تی‌اس‌ای، با کشِ ۵ دقیقه‌ای).",
        "parameters": [
          {
            "name": "symbol",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "آرایه‌ای از سهام‌داران (name, shares, percent, change, amount)"
          }
        }
      }
    },
    "/api/snapshot": {
      "get": {
        "tags": [
          "ابزار"
        ],
        "summary": "تصویر لحظه‌ای بازار",
        "description": "همان داده‌ای که از WebSocket پخش می‌شود: نمای کلی، پهنا، جریان پول و برترین ارزش.",
        "responses": {
          "200": {
            "description": "اسنپ‌شات کامل بازار"
          }
        }
      }
    },
    "/api/sparklines": {
      "get": {
        "tags": [
          "بازار"
        ],
        "summary": "روندِ کوتاهِ ۲۰ روزِ اخیر را برای فهرستِ نمادها (پارامتر syms با کاما) می‌دهد",
        "description": "روندِ کوتاهِ ۲۰ روزِ اخیر را برای فهرستِ نمادها (پارامتر syms با کاما) می‌دهد.",
        "responses": {
          "200": {
            "description": "پاسخِ JSON"
          }
        }
      }
    },
    "/api/statements": {
      "get": {
        "tags": [
          "بازار"
        ],
        "summary": "صورت‌های مالیِ رسمیِ یک نماد را زنده از کدال می‌گیرد (اطلاعات و صورت‌های مالیِ…",
        "description": "صورت‌های مالیِ رسمیِ یک نماد را زنده از کدال می‌گیرد (اطلاعات و صورت‌های مالیِ میان‌دوره‌ای/سالانه) و ۳۰ دقیقه کش می‌کند.",
        "responses": {
          "200": {
            "description": "پاسخِ JSON"
          }
        }
      }
    },
    "/api/strategies": {
      "get": {
        "tags": [
          "استراتژی و بک‌تست"
        ],
        "summary": "جدولِ برتریِ استراتژی‌های بک‌تست‌شده روی کلِ بازار (مطالعه‌ی edge)",
        "description": "جدولِ برتریِ استراتژی‌های بک‌تست‌شده روی کلِ بازار (مطالعه‌ی edge). اسکنِ سنگینِ جهانِ نمادها توسطِ runStrategyWarmer در پس‌زمینه (با مهلتِ ۶۰ثانیه) محاسبه و کش می‌شود؛ این handler فقط کشِ آماده را سرو می‌کند تا هرگز زیرِ مهلتِ ۱۵ثانیه‌ایِ درخواست تایم‌اوت نشود. تا گرم‌شدنِ اولیه پاسخ آرایه‌ی خالی است.",
        "responses": {
          "200": {
            "description": "پاسخِ JSON"
          }
        }
      }
    },
    "/api/strategies/popularity": {
      "get": {
        "tags": [
          "استراتژی و بک‌تست"
        ],
        "summary": "رتبه‌بندیِ محبوبیتِ استراتژی‌ها",
        "description": "استراتژی‌ها را بر اساسِ تعدادِ اتصال به حساب‌هایِ کاغذیِ همهٔ کاربران (paper_strategy_attachments) رتبه‌بندی می‌کند؛ بدونِ افشایِ هویتِ کاربر.",
        "responses": {
          "200": {
            "description": "پاسخِ JSON"
          }
        }
      }
    },
    "/api/strategies/catalog": {
      "get": {
        "tags": [
          "استراتژی و بک‌تست"
        ],
        "summary": "فهرستِ دسته‌بندی‌شده‌ی استراتژی‌ها + متادیتای سازنده (اندیکاتورها و اپراتورها) را می‌دهد",
        "description": "فهرستِ دسته‌بندی‌شده‌ی استراتژی‌ها + متادیتای سازنده (اندیکاتورها و اپراتورها) را می‌دهد؛ ایستا و کش‌شده.",
        "responses": {
          "200": {
            "description": "پاسخِ JSON"
          }
        }
      }
    },
    "/api/strategies/def": {
      "get": {
        "tags": [
          "استراتژی و بک‌تست"
        ],
        "summary": "تعریفِ کاملِ یک استراتژی",
        "description": "تعریفِ کاملِ استراتژی را با slug برمی‌گرداند (۴۰۴ اگر یافت نشود).",
        "parameters": [
          {
            "name": "slug",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "شناسهٔ متنیِ استراتژی"
          }
        ],
        "responses": {
          "200": {
            "description": "پاسخِ JSON"
          }
        }
      }
    },
    "/api/strategies/registry": {
      "get": {
        "tags": [
          "استراتژی و بک‌تست"
        ],
        "summary": "کاتالوگِ رجیستریِ استراتژی‌ها",
        "description": "کاتالوگِ رجیستریِ استراتژی‌ها را برمی‌گرداند (کش ۳۰ دقیقه).",
        "responses": {
          "200": {
            "description": "پاسخِ JSON"
          }
        }
      }
    },
    "/api/strategies/signals": {
      "get": {
        "tags": [
          "استراتژی و بک‌تست"
        ],
        "summary": "نمادهایی که امروز شرطِ ورودِ یک استراتژی را فعال کرده‌اند",
        "description": "نمادهایی که امروز شرطِ ورودِ یک استراتژی را فعال کرده‌اند.",
        "responses": {
          "200": {
            "description": "پاسخِ JSON"
          }
        }
      }
    },
    "/api/strategies/validation": {
      "get": {
        "tags": [
          "استراتژی و بک‌تست"
        ],
        "summary": "اعتبارسنجیِ تاریخیِ کتابخانه‌ی استراتژی‌ها در افقِ طبیعیِ هر استراتژی",
        "description": "کارنامه‌ی روش‌شناختیِ هر استراتژی: ورود در بازگشاییِ نشستِ بعد (نه پایانیِ روزِ سیگنال که هنگامِ صدورِ سیگنال قابلِ اجرا نیست)، جهانِ نقدشونده‌ی point-in-time، مقایسه با میانگینِ مقطعیِ همان روزِ بازار، افقِ متناسب با حافظه‌ی قواعدِ خودِ استراتژی (۵/۱۰/۲۰/۶۰ نشست)، تفکیکِ زمانیِ ۶۰/۴۰ برای برون‌نمونه و تصحیحِ محافظه‌کارانه‌ی هم‌پوشانیِ بازده‌ها. محاسبه‌ی ~۱۳دقیقه‌ای را runStrategyValidationWarmer روزی یک‌بار انجام می‌دهد؛ این handler فقط کش را سرو می‌کند و تا گرم‌شدنِ نخست آرایه‌ی خالی برمی‌گرداند.",
        "responses": {
          "200": {
            "description": "پاسخِ JSON"
          }
        }
      }
    },
    "/api/strategy-builder/alerts": {
      "get": {
        "tags": [
          "استراتژی و بک‌تست"
        ],
        "summary": "هشدارهای استراتژیِ کاربر",
        "description": "GET فهرستِ هشدارهای استراتژیِ کاربر؛ POST ثبتِ هشدارِ جدید (با محدودیتِ نرخ).",
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "حداکثر تعداد"
          }
        ],
        "responses": {
          "200": {
            "description": "پاسخِ JSON"
          },
          "401": {
            "description": "نیازمندِ احراز هویت"
          }
        }
      }
    },
    "/api/strategy-builder/builds": {
      "get": {
        "tags": [
          "استراتژی و بک‌تست"
        ],
        "summary": "ساخته‌های استراتژیِ کاربر",
        "description": "GET فهرستِ ساخته‌های استراتژیِ کاربر؛ POST ثبتِ ساختِ جدید (با محدودیتِ نرخ).",
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "حداکثر تعداد"
          }
        ],
        "responses": {
          "200": {
            "description": "پاسخِ JSON"
          },
          "401": {
            "description": "نیازمندِ احراز هویت"
          }
        }
      }
    },
    "/api/strategy-builder/export": {
      "post": {
        "tags": [
          "استراتژی و بک‌تست"
        ],
        "summary": "برون‌بریِ دادهٔ استراتژیِ کاربر",
        "description": "همهٔ داده‌های استراتژیِ کاربر را برای برون‌بری خروجی می‌دهد.",
        "responses": {
          "200": {
            "description": "پاسخِ JSON"
          },
          "401": {
            "description": "نیازمندِ احراز هویت"
          }
        }
      }
    },
    "/api/strategy-builder/reports": {
      "get": {
        "tags": [
          "استراتژی و بک‌تست"
        ],
        "summary": "گزارش‌های استراتژیِ کاربر",
        "description": "گزارش‌های استراتژیِ کاربر را بر پایهٔ نوع و محدودیتِ تعداد می‌دهد.",
        "parameters": [
          {
            "name": "type",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "نوعِ گزارش"
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "حداکثر تعداد"
          }
        ],
        "responses": {
          "200": {
            "description": "پاسخِ JSON"
          },
          "401": {
            "description": "نیازمندِ احراز هویت"
          }
        }
      }
    },
    "/api/strategy-builder/screener": {
      "get": {
        "tags": [
          "استراتژی و بک‌تست"
        ],
        "summary": "اسکرینرِ استراتژی",
        "description": "اسکرینرِ یک استراتژی را روی جهانِ نمادها اجرا می‌کند (نیازمندِ احراز هویت، محدودیتِ نرخ).",
        "responses": {
          "200": {
            "description": "پاسخِ JSON"
          },
          "401": {
            "description": "نیازمندِ احراز هویت"
          }
        }
      }
    },
    "/api/strategy-portfolios": {
      "get": {
        "tags": [
          "استراتژی و بک‌تست"
        ],
        "summary": "پرتفویِ استراتژی (فاز ۶",
        "description": "پرتفویِ استراتژی (فاز ۶؛ نیازمندِ ورودِ کاربری) --- GET  → فهرستِ سبدهای ذخیره‌شده‌ی کاربر. POST → ساختِ سبدِ ترکیبی از استراتژی‌های انتخاب‌شده (+ ذخیره‌ی اختیاری با save=true).",
        "responses": {
          "200": {
            "description": "پاسخِ JSON"
          },
          "401": {
            "description": "نیازمندِ احراز هویت"
          }
        }
      }
    },
    "/api/strategy-portfolios/attach-paper": {
      "post": {
        "tags": [
          "استراتژی و بک‌تست"
        ],
        "summary": "سبدِ استراتژی را به یک حسابِ کاغذی متصل می‌کند",
        "description": "سبدِ استراتژی را به یک حسابِ کاغذی متصل می‌کند.",
        "responses": {
          "200": {
            "description": "پاسخِ JSON"
          },
          "401": {
            "description": "نیازمندِ احراز هویت"
          }
        }
      }
    },
    "/api/strategy-portfolios/backtest": {
      "get": {
        "tags": [
          "استراتژی و بک‌تست"
        ],
        "summary": "بک‌تستِ تاریخیِ سبدِ چنداستراتژی با Cash مشترک",
        "description": "بک‌تستِ تاریخیِ سبدِ چنداستراتژی با Cash مشترک.",
        "responses": {
          "200": {
            "description": "پاسخِ JSON"
          },
          "401": {
            "description": "نیازمندِ احراز هویت"
          }
        }
      },
      "post": {
        "tags": [
          "استراتژی و بک‌تست"
        ],
        "summary": "بک‌تستِ تاریخیِ سبدِ چنداستراتژی با Cash مشترک",
        "description": "بک‌تستِ تاریخیِ سبدِ چنداستراتژی با Cash مشترک. علاوه‌بر `attribution` (انتسابِ سود/زیان به تفکیکِ استراتژی)، پاسخ `attribution_by_sector` و `attribution_by_market` را هم می‌دهد: همان معیارها (attributed_pnl/attributed_fees/attributed_notional/contribution_to_return/contribution_share_pct/risk_contribution_pct/trades/win_rate) اما افرازشده به تفکیکِ `sector_name`/`market_name` هر معامله (نمادِ بدونِ صنعت/بازارِ قابلِ‌تشخیص با برچسبِ «سایر» گروه می‌شود؛ هر معامله فقط در یک گروه می‌افتد، پس مجموعِ attributed_pnl در این دو جدول با مجموعِ attribution برابر است).",
        "responses": {
          "200": {
            "description": "پاسخِ JSON"
          },
          "401": {
            "description": "نیازمندِ احراز هویت"
          }
        }
      }
    },
    "/api/strategy-portfolios/backtest-detail": {
      "get": {
        "tags": [
          "استراتژی و بک‌تست"
        ],
        "summary": "جزئیاتِ یک بک‌تستِ ذخیره‌شدهٔ سبد (با equity_curve/attribution کامل)",
        "description": "یک بک‌تستِ ذخیره‌شدهٔ سبدِ استراتژی را با شناسه برمی‌گرداند، شاملِ equity_curve و attributionِ کامل — برخلافِ GET روی /api/strategy-portfolios/backtest (فهرست) که این ستون‌های سنگین را عمداً برنمی‌گرداند. برای مقایسهٔ چند بک‌تستِ ذخیره‌شده (نمودارِ همپوشان) به کار می‌رود. مالکیت با user_id در Store اعمال می‌شود.",
        "parameters": [
          {
            "name": "id",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "شناسهٔ بک‌تستِ ذخیره‌شدهٔ سبد"
          }
        ],
        "responses": {
          "200": {
            "description": "پاسخِ JSON"
          },
          "401": {
            "description": "نیازمندِ احراز هویت"
          },
          "404": {
            "description": "بک‌تست یافت نشد"
          }
        }
      }
    },
    "/api/system/status": {
      "get": {
        "tags": [
          "بازار"
        ],
        "summary": "وضعیت عمومی سرویس و تازگی داده‌های بازار",
        "description": "نمای عمومی و پاک‌سازی‌شده‌ی وضعیت سرویس: فقط تازگی feedهایی که کاربر می‌بیند و یک وضعیت کلی (ok یا degraded). هیچ نام job، شمار ردیف، جدول ماتریالایز یا آمار موتور دیتابیس در آن نیست؛ نمای کامل فقط از /api/admin/system/status و با نقش ادمین در دسترس است.",
        "responses": {
          "200": {
            "description": "پاسخِ JSON",
            "content": {
              "application/json": {
                "example": {
                  "state": "ok",
                  "feeds": [
                    {
                      "feed": "latest",
                      "max_ts": "2026-07-28 09:13:23.216318+00"
                    },
                    {
                      "feed": "client_type_latest",
                      "max_ts": "2026-07-28 09:29:52.632492+00"
                    },
                    {
                      "feed": "market_breadth",
                      "max_ts": "2026-07-28 09:30:11.004102+00"
                    }
                  ]
                }
              }
            }
          }
        }
      }
    },
    "/api/tal": {
      "get": {
        "tags": [
          "بازار"
        ],
        "summary": "معاملاتِ عمده و بلوکی (TAL)",
        "description": "فهرستِ آخرین معاملاتِ عمده/بلوکیِ بازار را می‌دهد (کش کوتاه).",
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "حداکثر تعدادِ معاملات"
          }
        ],
        "responses": {
          "200": {
            "description": "پاسخِ JSON"
          }
        }
      }
    },
    "/api/technical/beta": {
      "get": {
        "tags": [
          "تحلیل تکنیکال"
        ],
        "summary": "بتا/آلفا/همبستگی/نوسانِ سالانه‌ی نماد را نسبت به شاخصِ کل برمی‌گرداند",
        "description": "بتا/آلفا/همبستگی/نوسانِ سالانه‌ی نماد را نسبت به شاخصِ کل برمی‌گرداند. آلفا و نوسانِ روزانه با ضریبِ ۲۵۲ (روزهای معاملاتی) سالانه می‌شوند و R² از مربعِ همبستگی به‌دست می‌آید. با کم‌تر از ۲۰ نقطه‌ی مشترک، insufficient برمی‌گردد.",
        "responses": {
          "200": {
            "description": "پاسخِ JSON"
          }
        }
      }
    },
    "/api/technical/correlation": {
      "get": {
        "tags": [
          "تحلیل تکنیکال"
        ],
        "summary": "ماتریسِ همبستگیِ نمادها",
        "description": "ماتریسِ همبستگیِ بازدهِ فهرستی از نمادها (حداکثر ۱۲ نماد) را محاسبه می‌کند.",
        "parameters": [
          {
            "name": "symbols",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "فهرستِ نمادها با کاما (حداکثر ۱۲)"
          }
        ],
        "responses": {
          "200": {
            "description": "پاسخِ JSON"
          }
        }
      }
    },
    "/api/technical/scan": {
      "get": {
        "tags": [
          "تحلیل تکنیکال"
        ],
        "summary": "اسکنِ تکنیکالِ بازار",
        "description": "اسکنِ سیگنال‌های تکنیکال روی نمادهای بازار (با کشِ ۵ دقیقه‌ای). هر ردیف فیلدِ `codal_days` هم دارد: سنِ (به روز) آخرین اطلاعیهٔ ثبت‌شدهٔ کدالِ همان نماد؛ `null` یعنی هیچ اطلاعیه‌ای ثبت نشده. پایهٔ فیلترِ «سنِ آخرین اطلاعیهٔ کدال» در صفحهٔ اسکنر (سمتِ کلاینت).",
        "parameters": [
          {
            "name": "market",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "بازار"
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "حداکثر تعدادِ نتیجه"
          }
        ],
        "responses": {
          "200": {
            "description": "پاسخِ JSON"
          }
        }
      }
    },
    "/api/technical/volume-profile": {
      "get": {
        "tags": [
          "تحلیل تکنیکال"
        ],
        "summary": "پروفایلِ حجم (Volume Profile)",
        "description": "پروفایلِ حجمِ یک نماد را در بازه‌های قیمتی (buckets) می‌سازد.",
        "parameters": [
          {
            "name": "symbol",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "نمادِ موردنظر"
          },
          {
            "name": "buckets",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "تعدادِ بازه‌های قیمتی"
          }
        ],
        "responses": {
          "200": {
            "description": "پاسخِ JSON"
          }
        }
      }
    },
    "/api/technical/vwap": {
      "get": {
        "tags": [
          "تحلیل تکنیکال"
        ],
        "summary": "سریِ VWAPِ لنگرشده‌ی یک نماد روی پنجره‌ی روزانه (برای هم‌پوشانی روی نمودار)",
        "description": "سریِ VWAPِ لنگرشده‌ی یک نماد روی پنجره‌ی روزانه (برای هم‌پوشانی روی نمودار).",
        "responses": {
          "200": {
            "description": "پاسخِ JSON"
          }
        }
      }
    },
    "/api/trade-plan": {
      "get": {
        "tags": [
          "بازار"
        ],
        "summary": "طرحِ معامله‌ی یک نماد: ورود/حد ضرر/دو هدف/نسبتِ R:R/کیفیتِ ستاپ",
        "description": "طرحِ معامله‌ی یک نماد: ورود/حد ضرر/دو هدف/نسبتِ R:R/کیفیتِ ستاپ.",
        "responses": {
          "200": {
            "description": "پاسخِ JSON"
          }
        }
      }
    },
    "/api/trades": {
      "get": {
        "tags": [
          "نمادها"
        ],
        "summary": "ریز معاملات نماد",
        "parameters": [
          {
            "name": "symbol",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "آخرین معاملات تیک‌به‌تیک"
          }
        }
      }
    },
    "/api/user/apikeys": {
      "get": {
        "tags": [
          "کاربر"
        ],
        "summary": "فهرستِ کلیدها (بدونِ مقدارِ خام)",
        "description": "فهرستِ کلیدها (بدونِ مقدارِ خام).",
        "responses": {
          "200": {
            "description": "پاسخِ JSON"
          },
          "401": {
            "description": "نیازمندِ احراز هویت"
          }
        }
      }
    },
    "/api/user/apikeys/create": {
      "post": {
        "tags": [
          "کاربر"
        ],
        "summary": "ساختِ کلیدِ API",
        "description": "کلیدِ API جدید می‌سازد؛ مقدارِ کاملِ کلید تنها یک‌بار در پاسخ بازگردانده می‌شود و سپس فقط هشِ آن نگه‌داری می‌شود.",
        "responses": {
          "200": {
            "description": "پاسخِ JSON"
          },
          "401": {
            "description": "نیازمندِ احراز هویت"
          }
        }
      }
    },
    "/api/user/apikeys/revoke": {
      "post": {
        "tags": [
          "کاربر"
        ],
        "summary": "ابطالِ کلیدِ API",
        "description": "کلیدِ API را باطل می‌کند.",
        "responses": {
          "200": {
            "description": "پاسخِ JSON"
          },
          "401": {
            "description": "نیازمندِ احراز هویت"
          }
        }
      }
    },
    "/api/user/cockpit": {
      "get": {
        "tags": [
          "کاربر"
        ],
        "summary": "نمای واحدِ پنلِ حرفه‌ای کاربر (Strategy Cockpit، فاز ۷) را می‌دهد: جمعِ حساب‌های کاغذی،…",
        "description": "نمای واحدِ پنلِ حرفه‌ای کاربر (Strategy Cockpit، فاز ۷) را می‌دهد: جمعِ حساب‌های کاغذی، سود/زیان، Drawdown، سفارش/موقعیت، اتصال‌های استراتژی و شمارشِ استراتژی/سبد/بک‌تست در یک فراخوانی. فقط خواندن؛ هیچ معامله‌ی واقعی.",
        "responses": {
          "200": {
            "description": "پاسخِ JSON"
          },
          "401": {
            "description": "نیازمندِ احراز هویت"
          }
        }
      }
    },
    "/api/user/delete": {
      "post": {
        "tags": [
          "کاربر"
        ],
        "summary": "حذفِ همیشگیِ حساب",
        "description": "حذفِ همیشگیِ حساب.",
        "responses": {
          "200": {
            "description": "پاسخِ JSON"
          },
          "401": {
            "description": "نیازمندِ احراز هویت"
          }
        }
      }
    },
    "/api/user/export": {
      "post": {
        "tags": [
          "کاربر"
        ],
        "summary": "برون‌بریِ کاملِ داده‌های شخصیِ کاربر (JSON)",
        "description": "برون‌بریِ کاملِ داده‌های شخصیِ کاربر (JSON).",
        "responses": {
          "200": {
            "description": "پاسخِ JSON"
          },
          "401": {
            "description": "نیازمندِ احراز هویت"
          }
        }
      }
    },
    "/api/user/notifications": {
      "get": {
        "tags": [
          "کاربر"
        ],
        "summary": "فهرستِ اعلان‌های کاربر",
        "description": "اعلان‌های کاربرِ احرازشده را فهرست می‌کند.",
        "responses": {
          "200": {
            "description": "پاسخِ JSON"
          },
          "401": {
            "description": "نیازمندِ احراز هویت"
          }
        }
      }
    },
    "/api/user/notifications/read": {
      "post": {
        "tags": [
          "کاربر"
        ],
        "summary": "علامتِ خوانده‌شدنِ اعلان‌ها",
        "description": "اعلان‌ها را خوانده‌شده علامت می‌زند.",
        "responses": {
          "200": {
            "description": "پاسخِ JSON"
          },
          "401": {
            "description": "نیازمندِ احراز هویت"
          }
        }
      }
    },
    "/api/user/portfolio": {
      "get": {
        "tags": [
          "کاربر"
        ],
        "summary": "سبدِ کاربر",
        "description": "سبدِ سرمایه‌گذاریِ ذخیره‌شدهٔ کاربر را برمی‌گرداند.",
        "responses": {
          "200": {
            "description": "پاسخِ JSON"
          },
          "401": {
            "description": "نیازمندِ احراز هویت"
          }
        }
      }
    },
    "/api/user/portfolio/delete": {
      "post": {
        "tags": [
          "کاربر"
        ],
        "summary": "حذفِ سبد",
        "description": "سبدِ کاربر را حذف می‌کند.",
        "responses": {
          "200": {
            "description": "پاسخِ JSON"
          },
          "401": {
            "description": "نیازمندِ احراز هویت"
          }
        }
      }
    },
    "/api/user/portfolio/history": {
      "get": {
        "tags": [
          "کاربر"
        ],
        "summary": "تاریخچهٔ ارزشِ سبد",
        "description": "روندِ تاریخیِ ارزشِ سبدِ کاربر را می‌دهد.",
        "responses": {
          "200": {
            "description": "پاسخِ JSON"
          },
          "401": {
            "description": "نیازمندِ احراز هویت"
          }
        }
      }
    },
    "/api/user/portfolio/save": {
      "post": {
        "tags": [
          "کاربر"
        ],
        "summary": "ذخیرهٔ سبد",
        "description": "سبدِ کاربر را ذخیره/به‌روزرسانی می‌کند.",
        "responses": {
          "200": {
            "description": "پاسخِ JSON"
          },
          "401": {
            "description": "نیازمندِ احراز هویت"
          }
        }
      }
    },
    "/api/user/portfolio/share": {
      "post": {
        "tags": [
          "کاربر"
        ],
        "summary": "اشتراکِ عمومیِ سبد",
        "description": "لینکِ اشتراکِ عمومیِ سبد را می‌سازد یا به‌روزرسانی می‌کند.",
        "responses": {
          "200": {
            "description": "پاسخِ JSON"
          },
          "401": {
            "description": "نیازمندِ احراز هویت"
          }
        }
      }
    },
    "/api/user/portfolio/snapshot": {
      "post": {
        "tags": [
          "کاربر"
        ],
        "summary": "ثبتِ اسنپ‌شاتِ سبد",
        "description": "اسنپ‌شاتِ ارزشِ فعلیِ سبد را ثبت می‌کند.",
        "responses": {
          "200": {
            "description": "پاسخِ JSON"
          },
          "401": {
            "description": "نیازمندِ احراز هویت"
          }
        }
      }
    },
    "/api/user/scan-filters": {
      "get": {
        "tags": [
          "کاربر"
        ],
        "summary": "filters",
        "description": "filters",
        "responses": {
          "200": {
            "description": "پاسخِ JSON"
          },
          "401": {
            "description": "نیازمندِ احراز هویت"
          }
        }
      }
    },
    "/api/user/scan-filters/delete": {
      "post": {
        "tags": [
          "کاربر"
        ],
        "summary": "filters/delete بدنه: {\"id\":5}",
        "description": "filters/delete بدنه: {\"id\":5}",
        "responses": {
          "200": {
            "description": "پاسخِ JSON"
          },
          "401": {
            "description": "نیازمندِ احراز هویت"
          }
        }
      }
    },
    "/api/user/scan-filters/save": {
      "post": {
        "tags": [
          "کاربر"
        ],
        "summary": "filters/save بدنه: {\"id\":0,\"name\":\"…\",\"payload\":{…}} (id=0 = جدید)",
        "description": "filters/save بدنه: {\"id\":0,\"name\":\"…\",\"payload\":{…}}  (id=0 = جدید)",
        "responses": {
          "200": {
            "description": "پاسخِ JSON"
          },
          "401": {
            "description": "نیازمندِ احراز هویت"
          }
        }
      }
    },
    "/api/user/scan-filters/share": {
      "post": {
        "tags": [
          "کاربر"
        ],
        "summary": "اشتراکِ فیلترِ اسکن",
        "description": "یک فیلترِ اسکنِ کاربر را با نام و بدنهٔ payload (و انقضای اختیاری) به‌اشتراک می‌گذارد؛ توکنِ خامِ اشتراک تنها یک‌بار در پاسخ بازگردانده می‌شود.",
        "responses": {
          "200": {
            "description": "پاسخِ JSON"
          },
          "401": {
            "description": "نیازمندِ احراز هویت"
          }
        }
      }
    },
    "/api/user/scan-filters/share/revoke": {
      "post": {
        "tags": [
          "کاربر"
        ],
        "summary": "filters/share/revoke {id}",
        "description": "filters/share/revoke {id}",
        "responses": {
          "200": {
            "description": "پاسخِ JSON"
          },
          "401": {
            "description": "نیازمندِ احراز هویت"
          }
        }
      }
    },
    "/api/user/scan-filters/shares": {
      "get": {
        "tags": [
          "کاربر"
        ],
        "summary": "filters/shares — فهرستِ لینک‌های کاربر (بدونِ tokenِ خام)",
        "description": "filters/shares — فهرستِ لینک‌های کاربر (بدونِ tokenِ خام).",
        "responses": {
          "200": {
            "description": "پاسخِ JSON"
          },
          "401": {
            "description": "نیازمندِ احراز هویت"
          }
        }
      }
    },
    "/api/user/targets": {
      "get": {
        "tags": [
          "کاربر"
        ],
        "summary": "اهدافِ قیمتیِ کاربر",
        "description": "اهدافِ قیمتیِ تعریف‌شدهٔ کاربر را فهرست می‌کند.",
        "responses": {
          "200": {
            "description": "پاسخِ JSON"
          },
          "401": {
            "description": "نیازمندِ احراز هویت"
          }
        }
      }
    },
    "/api/user/targets/delete": {
      "post": {
        "tags": [
          "کاربر"
        ],
        "summary": "حذفِ هدفِ قیمتی",
        "description": "هدفِ قیمتی را حذف می‌کند.",
        "responses": {
          "200": {
            "description": "پاسخِ JSON"
          },
          "401": {
            "description": "نیازمندِ احراز هویت"
          }
        }
      }
    },
    "/api/user/targets/save": {
      "post": {
        "tags": [
          "کاربر"
        ],
        "summary": "ذخیرهٔ هدفِ قیمتی",
        "description": "هدفِ قیمتیِ کاربر را ذخیره می‌کند.",
        "responses": {
          "200": {
            "description": "پاسخِ JSON"
          },
          "401": {
            "description": "نیازمندِ احراز هویت"
          }
        }
      }
    },
    "/api/user/watchlists": {
      "get": {
        "tags": [
          "کاربر"
        ],
        "summary": "دیده‌بان‌های کاربر",
        "description": "فهرستِ دیده‌بان‌های ذخیره‌شدهٔ کاربر را برمی‌گرداند.",
        "responses": {
          "200": {
            "description": "پاسخِ JSON"
          },
          "401": {
            "description": "نیازمندِ احراز هویت"
          }
        }
      }
    },
    "/api/user/watchlists/delete": {
      "post": {
        "tags": [
          "کاربر"
        ],
        "summary": "حذفِ دیده‌بان",
        "description": "دیده‌بانِ کاربر را حذف می‌کند.",
        "responses": {
          "200": {
            "description": "پاسخِ JSON"
          },
          "401": {
            "description": "نیازمندِ احراز هویت"
          }
        }
      }
    },
    "/api/user/watchlists/save": {
      "post": {
        "tags": [
          "کاربر"
        ],
        "summary": "ذخیرهٔ دیده‌بان",
        "description": "دیده‌بانِ کاربر را ذخیره/به‌روزرسانی می‌کند.",
        "responses": {
          "200": {
            "description": "پاسخِ JSON"
          },
          "401": {
            "description": "نیازمندِ احراز هویت"
          }
        }
      }
    },
    "/api/user/webhooks": {
      "get": {
        "tags": [
          "کاربر"
        ],
        "summary": "وب‌هوک‌های کاربر",
        "description": "وب‌هوک‌های تعریف‌شدهٔ کاربر را فهرست می‌کند.",
        "responses": {
          "200": {
            "description": "پاسخِ JSON"
          },
          "401": {
            "description": "نیازمندِ احراز هویت"
          }
        }
      }
    },
    "/api/user/webhooks/delete": {
      "post": {
        "tags": [
          "کاربر"
        ],
        "summary": "حذفِ وب‌هوک",
        "description": "وب‌هوکِ کاربر را حذف می‌کند.",
        "responses": {
          "200": {
            "description": "پاسخِ JSON"
          },
          "401": {
            "description": "نیازمندِ احراز هویت"
          }
        }
      }
    },
    "/api/user/webhooks/save": {
      "post": {
        "tags": [
          "کاربر"
        ],
        "summary": "ذخیرهٔ وب‌هوک",
        "description": "وب‌هوکِ کاربر را ذخیره می‌کند.",
        "responses": {
          "200": {
            "description": "پاسخِ JSON"
          },
          "401": {
            "description": "نیازمندِ احراز هویت"
          }
        }
      }
    },
    "/api/vitals": {
      "get": {
        "tags": [
          "بازار"
        ],
        "summary": "گزارشِ Core Web Vitalsِ سمتِ کلاینت (RUM) را می‌گیرد و در متریک‌های Prometheus تجمیع می‌کند…",
        "description": "گزارشِ Core Web Vitalsِ سمتِ کلاینت (RUM) را می‌گیرد و در متریک‌های Prometheus تجمیع می‌کند (#43). مقادیرِ نامعقول نادیده گرفته می‌شوند.",
        "responses": {
          "200": {
            "description": "پاسخِ JSON"
          }
        }
      }
    },
    "/api/vwap-intraday": {
      "get": {
        "tags": [
          "تحلیل تکنیکال"
        ],
        "summary": "سریِ VWAPِ درون‌روزیِ یک نماد در سطلِ ۱دقیقه‌ای (value/volumeِ تجمعیِ نشست از ticks)",
        "description": "سریِ VWAPِ درون‌روزیِ یک نماد در سطلِ ۱دقیقه‌ای، برای هم‌پوشانی روی نمودارِ تایم‌فریمِ درون‌روزی. برخلافِ /api/technical/vwap (لنگرشده‌ی چندروزه‌ی daily_history)، این یکی هر بار فقط یک نشست را می‌پوشاند و از value/volumeِ تجمعیِ ticks در همان نشست می‌آید — بدونِ نیاز به sum روی پنجره. پارامترها: symbol (الزامی)، date (اختیاری، YYYYMMDD یا YYYY-MM-DD؛ نبودش یعنی آخرین نشست).",
        "responses": {
          "200": {
            "description": "پاسخِ JSON"
          }
        }
      }
    },
    "/api/vwap-scan": {
      "get": {
        "tags": [
          "تحلیل تکنیکال"
        ],
        "summary": "رتبه‌بندیِ مقطعیِ فاصله‌ی قیمت تا VWAPِ پنجره برای همه‌ی نمادهای پرمعامله",
        "description": "رتبه‌بندیِ مقطعیِ فاصله‌ی قیمت تا VWAPِ پنجره برای همه‌ی نمادهای پرمعامله.",
        "responses": {
          "200": {
            "description": "پاسخِ JSON"
          }
        }
      }
    },
    "/api/user/signal-alerts": {
      "get": {
        "tags": [
          "کاربر"
        ],
        "summary": "تنظیماتِ هشدارِ سیگنالی",
        "description": "تنظیماتِ غربالِ پس‌زمینهٔ کاربر: {enabled, kinds[], thresholds{vol,power,near}, configured, watch_count}. watch_count شمارِ نمادهای دیده‌بانِ ابری است؛ صفر یعنی کارگر چیزی برای پایش ندارد.",
        "responses": {
          "200": {
            "description": "پاسخِ JSON"
          },
          "401": {
            "description": "نیازمندِ احراز هویت"
          }
        }
      },
      "post": {
        "tags": [
          "کاربر"
        ],
        "summary": "ذخیرهٔ تنظیماتِ هشدارِ سیگنالی",
        "description": "بدنه: {enabled, kinds: [smart|volume|ceiling|floor|power|div|high52|low52], thresholds: {vol, power, near}}. با enabled=true وضعیتِ فعلیِ بازار «دیده‌شده» ثبت می‌شود تا نخستین چرخه سیلِ اعلان نفرستد. اعلان‌ها غربال‌اند، نه توصیهٔ خرید.",
        "responses": {
          "200": {
            "description": "پاسخِ JSON"
          },
          "400": {
            "description": "تنظیماتِ نامعتبر"
          },
          "401": {
            "description": "نیازمندِ احراز هویت"
          }
        }
      }
    },
    "/api/wave/scenarios": {
      "get": {
        "tags": [
          "موج"
        ],
        "summary": "سناریوهای رتبه‌بندی‌شدهٔ شمارشِ موج",
        "description": "فرضیه‌های شمارشِ موجِ یک نماد (کلاسیک/نئوویو) با امتیاز، قواعد، فیبوناچی، هدف و ابطال. منبع: جدولِ wave_scenarios (پرشده توسطِ workerِ کالکتور)، نه محاسبهٔ درجا.",
        "parameters": [
          {
            "name": "symbol",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "engine",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "classic",
                "neowave"
              ],
              "default": "classic"
            }
          },
          {
            "name": "degree",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "default": "minor"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 5,
              "maximum": 20
            }
          }
        ],
        "responses": {
          "200": {
            "description": "{data:[Scenario], meta:{symbol,count,status,note,engine,degree,date,formula_version}} — دادهٔ خالی یعنی هنوز محاسبه نشده، نه خطا"
          },
          "400": {
            "description": "ورودیِ نامعتبر (نماد/engine/degree/pattern/limit ناشناخته یا خارج از بازه)"
          },
          "404": {
            "description": "نماد یافت نشد"
          }
        }
      }
    },
    "/api/wave/pivots": {
      "get": {
        "tags": [
          "موج"
        ],
        "summary": "نقاطِ چرخشِ استخراج‌شده",
        "description": "محاسبهٔ درجا با wave.Pivots/BuildMonowaves (تابعِ خالص، سبک)؛ کش‌شده ۱۵ دقیقه.",
        "parameters": [
          {
            "name": "symbol",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "method",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "zigzag_pct",
                "zigzag_atr",
                "fractal",
                "monowave"
              ],
              "default": "zigzag_pct"
            }
          },
          {
            "name": "degree",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "{data:{method,degree,status,pivots:[Pivot],note}, meta:{symbol,count,formula_version}}"
          },
          "400": {
            "description": "ورودیِ نامعتبر (نماد/engine/degree/pattern/limit ناشناخته یا خارج از بازه)"
          },
          "404": {
            "description": "نماد یافت نشد"
          }
        }
      }
    },
    "/api/wave/fib": {
      "get": {
        "tags": [
          "موج"
        ],
        "summary": "سطوح و خوشه‌هایِ فیبوناچیِ یک سناریو",
        "description": "از payloadِ ذخیره‌شدهٔ سناریو می‌آید؛ scenario خالی یعنی سناریوی رتبهٔ ۱ (پیش‌فرضِ classic/minor).",
        "parameters": [
          {
            "name": "symbol",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "scenario",
            "in": "query",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "{data:{levels:[FibLevel], clusters:[FibCluster]}, meta:{symbol,date,status,note,formula_version}}"
          },
          "400": {
            "description": "ورودیِ نامعتبر (نماد/engine/degree/pattern/limit ناشناخته یا خارج از بازه)"
          },
          "404": {
            "description": "نماد یافت نشد"
          }
        }
      }
    },
    "/api/wave/targets": {
      "get": {
        "tags": [
          "موج"
        ],
        "summary": "هدف/ابطال/کانالِ یک سناریو",
        "description": "از payloadِ ذخیره‌شدهٔ سناریو می‌آید؛ scenario خالی یعنی سناریوی رتبهٔ ۱ (پیش‌فرضِ classic/minor).",
        "parameters": [
          {
            "name": "symbol",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "scenario",
            "in": "query",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "{data:{targets:[TargetZone], invalidation:Invalidation|null, channels:[Channel]}, meta:{symbol,date,status,note,formula_version}}"
          },
          "400": {
            "description": "ورودیِ نامعتبر (نماد/engine/degree/pattern/limit ناشناخته یا خارج از بازه)"
          },
          "404": {
            "description": "نماد یافت نشد"
          }
        }
      }
    },
    "/api/wave/history": {
      "get": {
        "tags": [
          "موج"
        ],
        "summary": "تکاملِ شمارشِ برتر در زمان (سنجهٔ پایداری)",
        "parameters": [
          {
            "name": "symbol",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "engine",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "classic",
                "neowave"
              ],
              "default": "classic"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 90,
              "maximum": 500
            }
          }
        ],
        "responses": {
          "200": {
            "description": "{data:[{dt,pattern,current_wave,confidence,status}], meta:{symbol,engine,count,changes,stability,formula_version}}"
          },
          "400": {
            "description": "ورودیِ نامعتبر (نماد/engine/degree/pattern/limit ناشناخته یا خارج از بازه)"
          },
          "404": {
            "description": "نماد یافت نشد"
          }
        }
      }
    },
    "/api/wave/scan": {
      "get": {
        "tags": [
          "موج"
        ],
        "summary": "اسکنِ بازار برایِ الگو/موجِ جاریِ مشخص",
        "description": "pattern الزامی است (store.ScanWaveByPattern بدونِ الگویِ دقیق قابلِ‌اجرا نیست). فیلترِ market هنوز اعمال نمی‌شود.",
        "parameters": [
          {
            "name": "engine",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "classic",
                "neowave"
              ],
              "default": "classic"
            }
          },
          {
            "name": "pattern",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "wave",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "موجِ جاری (current_wave)"
          },
          {
            "name": "min_confidence",
            "in": "query",
            "schema": {
              "type": "number",
              "minimum": 0,
              "maximum": 1
            }
          },
          {
            "name": "market",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "پذیرفته می‌شود ولی فعلاً فیلتر نمی‌کند (meta.note)"
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 50,
              "maximum": 200
            }
          }
        ],
        "responses": {
          "200": {
            "description": "{data:[{symbol,ins_code,name,engine,pattern,pattern_fa,current_wave,confidence,status,dt}], meta:{count,filters,formula_version}}"
          },
          "400": {
            "description": "ورودیِ نامعتبر (نماد/engine/degree/pattern/limit ناشناخته یا خارج از بازه)"
          }
        }
      }
    },
    "/api/wave/registry": {
      "get": {
        "tags": [
          "موج"
        ],
        "summary": "فرادادهٔ پایدارِ موج (الگوها، درجه‌ها، قواعد، بلوغِ نئوویو)",
        "description": "ETagِ ثابت (هم‌سبکِ /api/indicators/registry). Neely River و Self-Adapting صریحاً unavailable‌اند.",
        "responses": {
          "200": {
            "description": "{data:{patterns,degrees,rules,neowave:{levels,unavailable},statuses}, meta:{formula_version}}"
          },
          "304": {
            "description": "If-None-Match برابر با ETagِ جاری"
          }
        }
      }
    },
    "/api/wave/multi-tf": {
      "get": {
        "tags": [
          "موج"
        ],
        "summary": "هم‌ترازیِ چند تایم‌فریم (روزانه/هفتگی/ماهانه)",
        "description": "محاسبهٔ درجا با engine.Analyze روی هر سهٔ تایم‌فریم (workerِ کالکتور فقط روزانه را ذخیره می‌کند)؛ کش‌شده ۳۰ دقیقه تا حلقهٔ سرور زیرِ بارِ سه‌برابریِ Analyze نرود.",
        "parameters": [
          {
            "name": "symbol",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "{data:{frames:[{tf,pattern,current_wave,dir,confidence,status}], aligned, alignment, dir}, meta:{symbol,note,formula_version}}"
          },
          "400": {
            "description": "ورودیِ نامعتبر (نماد/engine/degree/pattern/limit ناشناخته یا خارج از بازه)"
          },
          "404": {
            "description": "نماد یافت نشد"
          }
        }
      }
    },
    "/api/wave/index": {
      "get": {
        "tags": [
          "موج"
        ],
        "summary": "شمارشِ موجِ شاخص (کل / هم‌وزن / آیفکس)",
        "description": "تحلیلِ موج روی سریِ شاخص از index_daily. درخواست‌محور است نه ذخیره‌شده: فقط سه شاخص وجود دارد و workerِ شبانه آن‌ها را نمی‌سازد، پس نتیجه ۳۰ دقیقه کش می‌شود. خروجی دقیقاً هم‌شکلِ /api/wave/scenarios است.",
        "parameters": [
          {
            "name": "code",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "کدِ شاخص؛ خارج از فهرستِ شناخته‌شده ⇒ ۴۰۰"
          },
          {
            "name": "engine",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "classic",
                "neowave"
              ]
            }
          },
          {
            "name": "degree",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "{data:[Scenario], meta:{code,name,count,status,note,engine,degree,formula_version}}"
          },
          "400": {
            "description": "ورودیِ نامعتبر (نماد/engine/degree/pattern/limit ناشناخته یا خارج از بازه)"
          },
          "404": {
            "description": "نماد یافت نشد"
          }
        }
      }
    },
    "/api/user/wave/annotations": {
      "get": {
        "tags": [
          "موج"
        ],
        "summary": "فهرستِ شمارش‌هایِ دستیِ کاربر",
        "parameters": [
          {
            "name": "symbol",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "خالی یعنی همهٔ نمادها"
          }
        ],
        "responses": {
          "200": {
            "description": "{data:[{id,ins_code,title,engine,payload,is_public,created_at,updated_at}], meta}"
          },
          "401": {
            "description": "غیرمجاز (ورود لازم است)"
          }
        }
      },
      "post": {
        "tags": [
          "موج"
        ],
        "summary": "افزودنِ شمارشِ دستی",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "symbol",
                  "payload"
                ],
                "properties": {
                  "symbol": {
                    "type": "string"
                  },
                  "title": {
                    "type": "string"
                  },
                  "engine": {
                    "type": "string",
                    "enum": [
                      "classic",
                      "neowave"
                    ]
                  },
                  "payload": {
                    "type": "object"
                  },
                  "is_public": {
                    "type": "boolean"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "{data:{id}}"
          },
          "400": {
            "description": "ورودیِ نامعتبر (نماد/engine/degree/pattern/limit ناشناخته یا خارج از بازه)"
          },
          "401": {
            "description": "غیرمجاز (ورود لازم است)"
          }
        }
      },
      "put": {
        "tags": [
          "موج"
        ],
        "summary": "ویرایشِ شمارشِ دستی (داخلاً حذف+درجِ دوباره)",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "id",
                  "symbol",
                  "payload"
                ],
                "properties": {
                  "id": {
                    "type": "integer"
                  },
                  "symbol": {
                    "type": "string"
                  },
                  "title": {
                    "type": "string"
                  },
                  "engine": {
                    "type": "string",
                    "enum": [
                      "classic",
                      "neowave"
                    ]
                  },
                  "payload": {
                    "type": "object"
                  },
                  "is_public": {
                    "type": "boolean"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "{data:{id}} — id ممکن است با idِ ورودی فرق کند (حذف+درجِ دوباره)"
          },
          "400": {
            "description": "ورودیِ نامعتبر (نماد/engine/degree/pattern/limit ناشناخته یا خارج از بازه)"
          },
          "401": {
            "description": "غیرمجاز (ورود لازم است)"
          }
        }
      },
      "delete": {
        "tags": [
          "موج"
        ],
        "summary": "حذفِ شمارشِ دستی",
        "parameters": [
          {
            "name": "id",
            "in": "query",
            "required": true,
            "schema": {
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "{data:{deleted:true}}"
          },
          "400": {
            "description": "ورودیِ نامعتبر (نماد/engine/degree/pattern/limit ناشناخته یا خارج از بازه)"
          },
          "401": {
            "description": "غیرمجاز (ورود لازم است)"
          }
        }
      }
    },
    "/api/codal/assemblies": {
      "get": {
        "tags": [
          "کدال"
        ],
        "summary": "اطلاعیه‌های مجامع",
        "description": "اطلاعیه‌های حاوی «مجمع» در بازه‌ی اخیر، گروه‌بندی بر نماد با نوع مجمع (عادی/فوق‌العاده) استخراج‌شده از عنوان.",
        "parameters": [
          {
            "name": "days",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 60
            }
          }
        ],
        "responses": {
          "200": {
            "description": "آرایه‌ی اطلاعیه‌های مجمع"
          }
        }
      }
    },
    "/api/codal/capital-pipeline": {
      "get": {
        "tags": [
          "کدال"
        ],
        "summary": "پایپ‌لاین افزایش سرمایه",
        "description": "اطلاعیه‌های افزایش سرمایه با مرحله (پیشنهاد/مجوز/تصمیم مجمع/ثبت) از الگوی عنوان؛ آخرین وضعیت هر نماد.",
        "parameters": [
          {
            "name": "days",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 730
            }
          }
        ],
        "responses": {
          "200": {
            "description": "آرایه‌ی مراحل افزایش سرمایه به تفکیک نماد"
          }
        }
      }
    },
    "/api/codal/announcement-effect": {
      "get": {
        "tags": [
          "کدال"
        ],
        "summary": "اثرسنج اطلاعیه‌ها",
        "description": "میانگین و میانه‌ی بازده روز معاملاتی بعد به‌ازای هر دسته‌ی اطلاعیه در ۱۲ ماه اخیر.",
        "parameters": [],
        "responses": {
          "200": {
            "description": "آمار بازده به تفکیک دسته‌ی اطلاعیه"
          }
        }
      }
    },
    "/api/fundlab": {
      "get": {
        "tags": [
          "ابزارهای تحلیلی"
        ],
        "summary": "آزمایشگاه بنیادی تک‌نماد",
        "description": "پاکت تجمیعی: سری فصلی و حاشیه‌ها، TTM EPS/PE، PEG، فصلی‌بودن درآمد، کیفیت سود، CAGR سه‌ساله، دوپون تقریبی و پیش‌بینی تاریخ گزارش بعدی.",
        "parameters": [
          {
            "name": "symbol",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "شیء تجمیعی تحلیل بنیادی"
          }
        }
      }
    },
    "/api/fundslab/fixed-yield": {
      "get": {
        "tags": [
          "صندوق‌ها"
        ],
        "summary": "بازده مؤثر درآمد ثابت‌ها",
        "description": "نرخ سالانه‌ی مؤثر هر صندوق درآمد ثابت از رگرسیون log-NAV در ۹۰ روز اخیر.",
        "parameters": [],
        "responses": {
          "200": {
            "description": "رتبه‌بندی صندوق‌ها بر نرخ مؤثر"
          }
        }
      }
    },
    "/api/fundslab/risk": {
      "get": {
        "tags": [
          "صندوق‌ها"
        ],
        "summary": "رتبه‌بندی ریسک صندوق‌ها",
        "description": "شارپ، سورتینو و حداکثر افت از بازده روزانه‌ی NAV یک‌ساله.",
        "parameters": [],
        "responses": {
          "200": {
            "description": "آرایه‌ی سنجه‌های ریسک هر صندوق"
          }
        }
      }
    },
    "/api/fundslab/leverage-beta": {
      "get": {
        "tags": [
          "صندوق‌ها"
        ],
        "summary": "بتای صندوق‌های اهرمی",
        "description": "بتای بازده روزانه‌ی قیمت هر صندوق اهرمی نسبت به شاخص کل.",
        "parameters": [],
        "responses": {
          "200": {
            "description": "آرایه‌ی بتا به تفکیک صندوق"
          }
        }
      }
    },
    "/api/macro/nh-nl": {
      "get": {
        "tags": [
          "ابزارهای تحلیلی"
        ],
        "summary": "سقف/کف‌های تازه (NH−NL)",
        "description": "شمار نمادهای بسته‌شده در سقف/کف ۲۵۲روزه برای هر روز معاملاتی سال اخیر.",
        "parameters": [],
        "responses": {
          "200": {
            "description": "سری روزانه‌ی NH و NL"
          }
        }
      }
    },
    "/api/macro/ma-breadth": {
      "get": {
        "tags": [
          "ابزارهای تحلیلی"
        ],
        "summary": "پهنای بازار بالای میانگین‌ها",
        "description": "سری روزانه‌ی شمار نمادهای بالای SMA20 و SMA50 با مخرج هر روز.",
        "parameters": [],
        "responses": {
          "200": {
            "description": "سری روزانه‌ی پهنای میانگین‌ها"
          }
        }
      }
    },
    "/api/macro/liquidity-concentration": {
      "get": {
        "tags": [
          "ابزارهای تحلیلی"
        ],
        "summary": "تمرکز نقدشوندگی",
        "description": "سهم ارزش معاملات ۱۰ و ۲۰ نماد صدرنشین از کل ارزش هر روز.",
        "parameters": [],
        "responses": {
          "200": {
            "description": "سری روزانه‌ی تمرکز"
          }
        }
      }
    },
    "/api/macro/retail-participation": {
      "get": {
        "tags": [
          "ابزارهای تحلیلی"
        ],
        "summary": "مشارکت حقیقی‌ها",
        "description": "جمع روزانه‌ی ارزش خرید/فروش حقیقی و حقوقی کل بازار.",
        "parameters": [],
        "responses": {
          "200": {
            "description": "سری روزانه‌ی مشارکت"
          }
        }
      }
    },
    "/api/options/lab/calendar": {
      "get": {
        "tags": [
          "اختیار معامله"
        ],
        "summary": "تقویم سررسیدها",
        "description": "سررسیدهای بازار اختیار با شمارش معکوس، تعداد قرارداد و ارزش معاملات هر سررسید.",
        "parameters": [],
        "responses": {
          "200": {
            "description": "آرایه‌ی سررسیدها"
          }
        }
      }
    },
    "/api/options/lab/activity": {
      "get": {
        "tags": [
          "اختیار معامله"
        ],
        "summary": "نسبت فعالیت اختیار به پایه",
        "description": "رتبه‌بندی پایه‌ها بر نسبت ارزش معاملات اختیارها به ارزش معاملات خود پایه.",
        "parameters": [],
        "responses": {
          "200": {
            "description": "رتبه‌بندی پایه‌ها"
          }
        }
      }
    },
    "/api/options/lab/board": {
      "get": {
        "tags": [
          "اختیار معامله"
        ],
        "summary": "تخته‌ی سررسیدهای یک پایه",
        "description": "برای هر سررسید: اعمال ATM، قیمت استرادل، حرکت موردانتظار و بازه‌ی قیمتی.",
        "parameters": [
          {
            "name": "underlying",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "آرایه‌ی سررسیدها با حرکت موردانتظار"
          }
        }
      }
    },
    "/api/options/lab/vol": {
      "get": {
        "tags": [
          "اختیار معامله"
        ],
        "summary": "IV در برابر HV",
        "description": "سری روزانه‌ی IV نزدیک‌به‌پول (بازسازی‌شده از تاریخچه‌ی قراردادها) و HV20 پایه.",
        "parameters": [
          {
            "name": "underlying",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "سری IV و HV"
          }
        }
      }
    },
    "/api/quant-lab/shape": {
      "get": {
        "tags": [
          "ابزارهای تحلیلی"
        ],
        "summary": "غربال شکل توزیع بازده",
        "description": "چولگی و کشیدگی بازده ۲۵۰ نشست اخیر روی استخر نمادهای پرارزش؛ دو فهرست بخت‌آزمایی/خطرناک.",
        "parameters": [],
        "responses": {
          "200": {
            "description": "دو فهرست رتبه‌بندی‌شده"
          }
        }
      }
    },
    "/api/quant-lab/ulcer": {
      "get": {
        "tags": [
          "ابزارهای تحلیلی"
        ],
        "summary": "رتبه‌بندی شاخص زخم",
        "description": "Ulcer Index یک‌ساله به‌همراه حداکثر افت و سهم روزهای زیر سقف.",
        "parameters": [],
        "responses": {
          "200": {
            "description": "رتبه‌بندی نمادها"
          }
        }
      }
    },
    "/api/screen/extras": {
      "get": {
        "tags": [
          "غربالگر"
        ],
        "summary": "غربال‌های پایان‌روزی تازه",
        "description": "هشت غربال: nr7، tight، stretch، near-ath، near-atl، legal-streak، reopened، ipo.",
        "parameters": [
          {
            "name": "kind",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "حداکثر ۱۰۰ ردیف نتیجه‌ی غربال"
          }
        }
      }
    },
    "/api/screen/similar": {
      "get": {
        "tags": [
          "غربالگر"
        ],
        "summary": "یابنده‌ی نماد مشابه",
        "description": "۱۵ نماد با بیشترین همبستگی پیرسون بازده ۱۲۰ نشست اخیر با نماد ورودی.",
        "parameters": [
          {
            "name": "code",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "آرایه‌ی نمادهای مشابه با ضریب همبستگی"
          }
        }
      }
    },
    "/api/tape-lab/code-radar": {
      "get": {
        "tags": [
          "ابزارهای تحلیلی"
        ],
        "summary": "رادار کد به کد",
        "description": "روزهایی که فروش حقوقی و خرید حقیقی هر دو بیش از ۷۰٪ حجم روز و ارزش روز بیش از ۲ برابر میانگین ۲۰روزه بوده.",
        "parameters": [
          {
            "name": "days",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 30
            }
          }
        ],
        "responses": {
          "200": {
            "description": "آرایه‌ی رویدادهای مشکوک به کد به کد"
          }
        }
      }
    },
    "/api/tape-lab/big-money": {
      "get": {
        "tags": [
          "ابزارهای تحلیلی"
        ],
        "summary": "سهم پول درشت",
        "description": "سری روزانه‌ی سهم ارزش معاملات درشت از کل، برای یک نماد.",
        "parameters": [
          {
            "name": "symbol",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "سری روزانه‌ی سهم پول درشت"
          }
        }
      }
    },
    "/api/tape-lab/volume-curve": {
      "get": {
        "tags": [
          "ابزارهای تحلیلی"
        ],
        "summary": "منحنی ساعتی حجم",
        "description": "میانگین سهم هر بازه‌ی ۳۰دقیقه‌ای از حجم نشست روی ۲۰ جلسه‌ی اخیر.",
        "parameters": [
          {
            "name": "symbol",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "توزیع درون‌روزی حجم"
          }
        }
      }
    },
    "/api/tape-lab/closing-rush": {
      "get": {
        "tags": [
          "ابزارهای تحلیلی"
        ],
        "summary": "اسکنر دقایق پایانی",
        "description": "نمادهایی که بیش از ۳۰٪ حجم نشست جاری‌شان در ۳۰ دقیقه‌ی پایانی معامله شده.",
        "parameters": [],
        "responses": {
          "200": {
            "description": "آرایه‌ی نمادها با سهم دقایق پایانی"
          }
        }
      }
    },
    "/api/tape-lab/queue-profile": {
      "get": {
        "tags": [
          "ابزارهای تحلیلی"
        ],
        "summary": "پروفایل صف",
        "description": "درصد زمان قفل روی آستانه‌ی بالا/پایین دامنه در ۲۰ نشست اخیر.",
        "parameters": [
          {
            "name": "symbol",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "آمار صف‌نشینی نماد"
          }
        }
      }
    },
    "/api/timemachine": {
      "get": {
        "tags": [
          "ابزارهای تحلیلی"
        ],
        "summary": "تابلوی یک روز گذشته",
        "description": "تابلوی معاملات یک روز مشخص از تاریخچه: تا ۶۰۰ نماد مرتب بر ارزش + خلاصه‌ی بازار.",
        "parameters": [
          {
            "name": "date",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "تابلوی روز و خلاصه"
          }
        }
      }
    },
    "/api/records": {
      "get": {
        "tags": [
          "ابزارهای تحلیلی"
        ],
        "summary": "رکوردبان بازار",
        "description": "رکوردهای تاریخی: پرارزش‌ترین روز، بیشترین تعداد معامله، بزرگ‌ترین رشد/افت شاخص و قدمت داده.",
        "parameters": [],
        "responses": {
          "200": {
            "description": "شیء رکوردهای تاریخی"
          }
        }
      }
    },
    "/api/screen/metrics": {
      "get": {
        "tags": [
          "غربالگر"
        ],
        "summary": "متریک‌های خام غربال وزنی",
        "description": "برای ۳۰۰ نماد پرارزش روز، ۱۲ متریک خام رتبه‌بندی (امتیاز روند، RSI۱۴، پهنای باند، فاصله از MA50/MA200، بازده ۲۰/۶۰ روزه، فاصله تا سقف/کف ۵۲هفته، حجم نسبی، P/E، P/S، صنعت) — وزن‌دهی سمت مرورگر انجام می‌شود.",
        "parameters": [],
        "responses": {
          "200": {
            "description": "پاسخ JSON"
          }
        }
      }
    },
    "/api/tape-lab/thermometer": {
      "get": {
        "tags": [
          "ابزارهای تحلیلی"
        ],
        "summary": "دماسنج هیجان بازار",
        "description": "نمرهٔ ۰–۱۰۰ از ترکیب RVOL بازار، دامنهٔ نوسان میانه و فشار صف‌ها با برچسب سرد تا داغ.",
        "parameters": [],
        "responses": {
          "200": {
            "description": "پاسخ JSON"
          }
        }
      }
    },
    "/api/tape-lab/queue-lock": {
      "get": {
        "tags": [
          "ابزارهای تحلیلی"
        ],
        "summary": "احتمال قفل صف",
        "description": "نمرهٔ قاعده‌محورِ احتمال قفل‌شدن صف هر نماد از جای قیمت در دامنه، حجم صف نسبت به میانگین و سرانهٔ صف.",
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer"
            },
            "description": "سقف ردیف‌ها (پیش‌فرض ۸۰، حداکثر ۳۰۰)"
          }
        ],
        "responses": {
          "200": {
            "description": "پاسخ JSON"
          }
        }
      }
    },
    "/api/tape-lab/real-per-capita": {
      "get": {
        "tags": [
          "ابزارهای تحلیلی"
        ],
        "summary": "سرانهٔ زندهٔ خرید و فروش حقیقی",
        "description": "جدول زندهٔ سرانهٔ خرید/فروش حقیقی کل بازار مرتب بر سرانهٔ خرید.",
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer"
            },
            "description": "سقف ردیف‌ها"
          },
          {
            "name": "min_buyers",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer"
            },
            "description": "حداقل شمار کد خریدار حقیقی"
          }
        ],
        "responses": {
          "200": {
            "description": "پاسخ JSON"
          }
        }
      }
    },
    "/api/tape-lab/microstructure": {
      "get": {
        "tags": [
          "ابزارهای تحلیلی"
        ],
        "summary": "ریزساختار دفترچهٔ سفارش",
        "description": "نامزدهای سفارش کوه‌یخی و سنجهٔ تاب‌آوری صف از تاریخچهٔ دفترچه؛ پیش‌محاسبه‌شده — اگر کش تهی باشد warming:true برمی‌گرداند.",
        "parameters": [],
        "responses": {
          "200": {
            "description": "پاسخ JSON"
          }
        }
      }
    },
    "/api/tape-lab/spread": {
      "get": {
        "tags": [
          "ابزارهای تحلیلی"
        ],
        "summary": "آمار اسپرد نماد",
        "description": "میانگین/میانه/صدک۹۰ فاصلهٔ بهترین خرید و فروش یک نماد در روزهای اخیر.",
        "parameters": [
          {
            "name": "symbol",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "نماد"
          },
          {
            "name": "days",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer"
            },
            "description": "شمار روز (۲۰ یا ۶۰)"
          }
        ],
        "responses": {
          "200": {
            "description": "پاسخ JSON"
          }
        }
      }
    },
    "/api/tape-lab/opening-auction": {
      "get": {
        "tags": [
          "ابزارهای تحلیلی"
        ],
        "summary": "تحلیل حراج آغازین",
        "description": "گپ باز شدن در برابر بازدهٔ باقی روز و نرخ هم‌جهتی برای یک نماد.",
        "parameters": [
          {
            "name": "symbol",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "نماد"
          },
          {
            "name": "days",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer"
            },
            "description": "شمار روز"
          }
        ],
        "responses": {
          "200": {
            "description": "پاسخ JSON"
          }
        }
      }
    },
    "/api/fundlab/quality": {
      "get": {
        "tags": [
          "کدال"
        ],
        "summary": "کیفیت سود و F-Score",
        "description": "F-Score پیوتروسکی نسخهٔ مقدور (وضعیت سه‌حالتهٔ هر جزء)، شش پرچم هشداری کیفیت سود و EPS تعدیل‌شده به سرمایهٔ امروز.",
        "parameters": [
          {
            "name": "symbol",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "نماد"
          }
        ],
        "responses": {
          "200": {
            "description": "پاسخ JSON"
          }
        }
      }
    },
    "/api/fundlab/earnings-quality": {
      "get": {
        "tags": [
          "کدال"
        ],
        "summary": "سنجهٔ کیفیت سود — اقلام زیر خط عملیاتی",
        "description": "سهم «اقلام زیر خط عملیاتی» (تفاضل سود خالص و سود عملیاتی، شامل هزینهٔ مالی و مالیات) از سود خالص در هر فصل، همراه با میانه و MAD تاریخی خود نماد و پرچم یک‌بارمصرف بر پایهٔ z مقاوم (آستانهٔ ۳٫۵، حداقل ۶ فصل).",
        "parameters": [
          {
            "name": "symbol",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "نماد"
          }
        ],
        "responses": {
          "200": {
            "description": "پاسخ JSON"
          }
        }
      }
    },
    "/api/fundlab/dividends": {
      "get": {
        "tags": [
          "کدال"
        ],
        "summary": "تاریخچهٔ سود نقدی و بازده نقدی",
        "description": "با symbol تاریخچهٔ رویدادهای سود نقدی برآوردی از تعدیل قیمت؛ بدون symbol غربال بازده نقدی بالای بازار.",
        "parameters": [
          {
            "name": "symbol",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "نماد (اختیاری)"
          }
        ],
        "responses": {
          "200": {
            "description": "پاسخ JSON"
          }
        }
      }
    },
    "/api/fundlab/sectors": {
      "get": {
        "tags": [
          "کدال"
        ],
        "summary": "بنیادی تجمیعی صنایع",
        "description": "میانه و میانگین P/E، حاشیهٔ خالص و رشد سود هر صنعت از صورت‌های مالی کدال.",
        "parameters": [],
        "responses": {
          "200": {
            "description": "پاسخ JSON"
          }
        }
      }
    },
    "/api/fundlab/monthly": {
      "get": {
        "tags": [
          "کدال"
        ],
        "summary": "پایشگر گزارش فعالیت ماهانه",
        "description": "آخرین ماه گزارش‌شدهٔ هر ناشر و فهرست نمادهای دیرکرده (فقط عنوان/تاریخ اطلاعیه؛ بدون اعداد فروش).",
        "parameters": [],
        "responses": {
          "200": {
            "description": "پاسخ JSON"
          }
        }
      }
    },
    "/api/fundlab/monthly-sales": {
      "get": {
        "tags": [
          "کدال"
        ],
        "summary": "ارقام گزارش فعالیت ماهانه",
        "description": "مبلغ ماهانه و تجمعی سال مالی از خودِ گزارش فعالیت ماهانهٔ کدال، به‌همراه رشد ماه‌به‌ماه و سال‌به‌سال. با symbol سری یک نماد و بدون آن غربال بازار روی تازه‌ترین ماهِ هر نماد. سه نوع نامه (تولیدی/بانکی/هلدینگ) هم‌جنس نیستند و با kind تفکیک می‌شوند.",
        "parameters": [
          {
            "name": "symbol",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "نماد؛ اگر خالی باشد غربال بازار برگردانده می‌شود"
          },
          {
            "name": "kind",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "sales",
                "bank_revenue",
                "operating_income",
                "service_revenue",
                "realized_income"
              ]
            },
            "description": "نوع رقم ماهانه — فقط در حالت غربال بازار"
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer"
            },
            "description": "سقف ردیف‌های غربال"
          }
        ],
        "responses": {
          "200": {
            "description": "پاسخ JSON"
          }
        }
      }
    },
    "/api/fundlab/notices": {
      "get": {
        "tags": [
          "کدال"
        ],
        "summary": "تغییر سال مالی و حسابرس",
        "description": "اطلاعیه‌های کدال با مضمون تغییر سال مالی یا تغییر/تعیین حسابرس.",
        "parameters": [],
        "responses": {
          "200": {
            "description": "پاسخ JSON"
          }
        }
      }
    },
    "/api/fundlab/compare": {
      "get": {
        "tags": [
          "کدال"
        ],
        "summary": "مقایسهٔ بنیادی چند نماد",
        "description": "P/E، P/S، حاشیه‌ها، رشد سود و EPS آخرین سال مالی برای تا ۱۲ نماد.",
        "parameters": [
          {
            "name": "symbols",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "نمادها با جداکنندهٔ کاما"
          }
        ],
        "responses": {
          "200": {
            "description": "پاسخ JSON"
          }
        }
      }
    },
    "/api/fundslab/aum": {
      "get": {
        "tags": [
          "صندوق‌ها"
        ],
        "summary": "دارایی تحت مدیریت صندوق‌ها",
        "description": "عکس لحظه‌ای AUM هر صندوق (NAV × واحدهای جاری) مرتب‌شده نزولی.",
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer"
            },
            "description": "سقف ردیف‌ها"
          }
        ],
        "responses": {
          "200": {
            "description": "پاسخ JSON"
          }
        }
      }
    },
    "/api/fundslab/flow": {
      "get": {
        "tags": [
          "صندوق‌ها"
        ],
        "summary": "جریان تقریبی صدور و ابطال",
        "description": "برآورد تقریبی جریان ورود/خروج صندوق‌ها از ارزش معاملات و حباب روزانه.",
        "parameters": [
          {
            "name": "days",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer"
            },
            "description": "بازهٔ روز"
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer"
            },
            "description": "سقف ردیف‌ها"
          }
        ],
        "responses": {
          "200": {
            "description": "پاسخ JSON"
          }
        }
      }
    },
    "/api/macrox/size-flow": {
      "get": {
        "tags": [
          "ابزارهای تحلیلی"
        ],
        "summary": "جریان پول بین طبقات اندازه",
        "description": "سری روزانهٔ ارزش معاملات، شمار نماد و بازدهٔ میانگین به تفکیک چهار طبقهٔ نقدشوندگی در ۲۵۰ نشست اخیر.",
        "parameters": [],
        "responses": {
          "200": {
            "description": "پاسخ JSON"
          }
        }
      }
    },
    "/api/macrox/treasury-curve": {
      "get": {
        "tags": [
          "ابزارهای تحلیلی"
        ],
        "summary": "منحنی بازده اسناد خزانه",
        "description": "آخرین قیمت هر نماد اخزا با سررسید استخراج‌شده از نام، روز باقی‌مانده و YTM مؤثر سالانهٔ صفرکوپن.",
        "parameters": [],
        "responses": {
          "200": {
            "description": "پاسخ JSON"
          }
        }
      }
    },
    "/api/macrox/week-ahead": {
      "get": {
        "tags": [
          "ابزارهای تحلیلی"
        ],
        "summary": "هفتهٔ پیش رو",
        "description": "مجامع تاریخ‌دار ۲۱ روز آینده، دعوت‌نامه‌های تازه، تازه‌واردهای بازار و برآورد گزارش‌های ماهانهٔ منتظره.",
        "parameters": [],
        "responses": {
          "200": {
            "description": "پاسخ JSON"
          }
        }
      }
    },
    "/api/macrox/vol-index": {
      "get": {
        "tags": [
          "ابزارهای تحلیلی"
        ],
        "summary": "شاخص نوسان تحقق‌یافته",
        "description": "نوسان تحقق‌یافتهٔ ۲۰نشستهٔ شاخص کل (سالانه‌شده) به‌همراه میانگین عرض دامنهٔ روزانه.",
        "parameters": [],
        "responses": {
          "200": {
            "description": "پاسخ JSON"
          }
        }
      }
    },
    "/api/user/alert-rules": {
      "get": {
        "tags": [
          "حساب کاربری"
        ],
        "summary": "قاعده‌های هشدار سمت سرور",
        "description": "فهرست قاعده‌های هشدار کاربر + فراداده (نوع‌ها، دسته‌های کدال، شاخص‌ها، ساعت سکوت). نیازمند ورود.",
        "parameters": [],
        "responses": {
          "200": {
            "description": "فهرست قاعده‌ها"
          }
        }
      },
      "post": {
        "tags": [
          "حساب کاربری"
        ],
        "summary": "ساخت/تغییر قاعدهٔ هشدار",
        "description": "ساخت قاعدهٔ تازه {kind,symbol,cond}، تغییر وضعیت {action:\"toggle\"} یا سقف روزانه {action:\"cap\"}. نیازمند ورود.",
        "responses": {
          "200": {
            "description": "نتیجه"
          }
        }
      },
      "delete": {
        "tags": [
          "حساب کاربری"
        ],
        "summary": "حذف قاعدهٔ هشدار",
        "description": "حذف قاعده با ?id=. نیازمند ورود.",
        "parameters": [
          {
            "name": "id",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "شناسهٔ قاعده"
          }
        ],
        "responses": {
          "200": {
            "description": "نتیجه"
          }
        }
      }
    },
    "/api/user/alert-rules/report": {
      "get": {
        "tags": [
          "حساب کاربری"
        ],
        "summary": "گزارش عملکرد هشدارها",
        "description": "رویدادهای فعال‌شدن هر قاعده + بازدهٔ یک روز بعد و نرخ نتایج مثبت. نیازمند ورود.",
        "parameters": [
          {
            "name": "days",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer"
            },
            "description": "بازهٔ روز (پیش‌فرض ۶۰)"
          }
        ],
        "responses": {
          "200": {
            "description": "پاسخ JSON"
          }
        }
      }
    },
    "/api/portfolio-x/factor-xray": {
      "get": {
        "tags": [
          "ابزارهای تحلیلی"
        ],
        "summary": "اشعهٔ ایکس فاکتوری پرتفوی",
        "description": "صدک ارزش/مومنتوم/کیفیت/اندازهٔ نمادهای پرتفوی نسبت به جهان نقدشوندهٔ بازار.",
        "parameters": [
          {
            "name": "symbols",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "نمادها با کاما (تا ۲۵)"
          }
        ],
        "responses": {
          "200": {
            "description": "پاسخ JSON"
          }
        }
      }
    },
    "/api/portfolio-x/risk-contribution": {
      "get": {
        "tags": [
          "ابزارهای تحلیلی"
        ],
        "summary": "سهم از ریسک پرتفوی",
        "description": "ماتریس کوواریانس بازدهٔ ۹۰ نشست مشترک + نوسان هر نماد؛ وزن‌ها به سرور نمی‌روند.",
        "parameters": [
          {
            "name": "symbols",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "نمادها با کاما"
          }
        ],
        "responses": {
          "200": {
            "description": "پاسخ JSON"
          }
        }
      }
    },
    "/api/portfolio-x/stress": {
      "get": {
        "tags": [
          "ابزارهای تحلیلی"
        ],
        "summary": "آزمون بحران تاریخی",
        "description": "بازدهٔ واقعی نمادها در چهار بحران تاریخی بورس تهران + بدترین پنجرهٔ ۶۰نشسته و بتای جایگزین.",
        "parameters": [
          {
            "name": "symbols",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "نمادها با کاما"
          }
        ],
        "responses": {
          "200": {
            "description": "پاسخ JSON"
          }
        }
      }
    },
    "/api/portfolio-x/dividends": {
      "get": {
        "tags": [
          "ابزارهای تحلیلی"
        ],
        "summary": "تقویم سود نقدی پرتفوی",
        "description": "اطلاعیه‌های مجمع/تقسیم سود کدال و رویدادهای سود نقدی برآوردی نمادهای پرتفوی.",
        "parameters": [
          {
            "name": "symbols",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "نمادها با کاما"
          }
        ],
        "responses": {
          "200": {
            "description": "پاسخ JSON"
          }
        }
      }
    },
    "/api/sector-lab/sectors": {
      "get": {
        "tags": [
          "ابزارهای تحلیلی"
        ],
        "summary": "فهرست صنایع",
        "description": "صنایع بازار با شمار اعضا و ارزش معاملات برای منوی آزمایشگاه صنایع.",
        "parameters": [],
        "responses": {
          "200": {
            "description": "پاسخ JSON"
          }
        }
      }
    },
    "/api/sector-lab/profile": {
      "get": {
        "tags": [
          "ابزارهای تحلیلی"
        ],
        "summary": "پروفایل صنعت",
        "description": "شاخص هم‌وزن صنعت، ارزش معاملات روزانه، بزرگ‌ترین اعضا و برترین/بدترین ۳۰ روز.",
        "parameters": [
          {
            "name": "sector",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "کد یا نام صنعت"
          },
          {
            "name": "days",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer"
            },
            "description": "بازهٔ روز"
          }
        ],
        "responses": {
          "200": {
            "description": "پاسخ JSON"
          }
        }
      }
    },
    "/api/sector-lab/breadth": {
      "get": {
        "tags": [
          "ابزارهای تحلیلی"
        ],
        "summary": "پهنای درون‌صنعتی",
        "description": "درصد اعضای بالای MA50، درصد مثبت روز و خط AD تجمعی صنعت.",
        "parameters": [
          {
            "name": "sector",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "کد یا نام صنعت"
          },
          {
            "name": "days",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer"
            },
            "description": "بازهٔ روز"
          }
        ],
        "responses": {
          "200": {
            "description": "پاسخ JSON"
          }
        }
      }
    },
    "/api/sector-lab/monthly": {
      "get": {
        "tags": [
          "ابزارهای تحلیلی"
        ],
        "summary": "ماتریس بازده ماهانهٔ صنایع",
        "description": "بازده ماهانهٔ شاخص هم‌وزن هر صنعت در ۲۴ ماه شمسی اخیر برای نقشهٔ حرارتی.",
        "parameters": [],
        "responses": {
          "200": {
            "description": "پاسخ JSON"
          }
        }
      }
    },
    "/api/sector-lab/leadlag": {
      "get": {
        "tags": [
          "ابزارهای تحلیلی"
        ],
        "summary": "پیش‌رو/پس‌رو صنایع",
        "description": "ماتریس همبستگی بازده هفتگی ۲۰ صنعت بزرگ با تأخیر ۰ و ۱ + نمرهٔ پیش‌رو بودن.",
        "parameters": [],
        "responses": {
          "200": {
            "description": "پاسخ JSON"
          }
        }
      }
    },
    "/api/paper/season-leaderboard": {
      "get": {
        "tags": [
          "ابزارهای تحلیلی"
        ],
        "summary": "لیدربورد فصلی معاملهٔ کاغذی",
        "description": "رتبه‌بندی حساب‌های opt-in بر پایهٔ سود محقق‌شدهٔ فصل جلالی.",
        "parameters": [
          {
            "name": "season",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "فصل به شکل jy-jq مثل 1404-2"
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer"
            },
            "description": "سقف ردیف‌ها"
          }
        ],
        "responses": {
          "200": {
            "description": "پاسخ JSON"
          }
        }
      }
    },
    "/api/paper/trader-profile": {
      "get": {
        "tags": [
          "ابزارهای تحلیلی"
        ],
        "summary": "پروفایل عمومی معامله‌گر کاغذی",
        "description": "نمای عمومی یک حساب opt-in: بازده کل، شمار معامله، نرخ برد و معاملات اخیر.",
        "parameters": [
          {
            "name": "account_id",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "شناسهٔ حساب"
          }
        ],
        "responses": {
          "200": {
            "description": "پاسخ JSON"
          }
        }
      }
    },
    "/api/wave/gallery": {
      "get": {
        "tags": [
          "ابزارهای تحلیلی"
        ],
        "summary": "گالری عمومی موج‌شماری",
        "description": "شمارش‌های موج الیوت که کاربرانشان عمومی کرده‌اند (بدون شناسهٔ کاربر).",
        "parameters": [
          {
            "name": "symbol",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "فیلتر نماد"
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer"
            },
            "description": "سقف ردیف‌ها"
          }
        ],
        "responses": {
          "200": {
            "description": "پاسخ JSON"
          }
        }
      }
    },
    "/api/rss/codal.xml": {
      "get": {
        "tags": [
          "کدال"
        ],
        "summary": "خوراک RSS اطلاعیه‌های کدال",
        "description": "RSS 2.0 از ۵۰ اطلاعیهٔ اخیر کدال.",
        "parameters": [
          {
            "name": "symbol",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "فیلتر نماد"
          },
          {
            "name": "category",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "دسته: capital|transparency|assembly|adjustment"
          }
        ],
        "responses": {
          "200": {
            "description": "سند XML خوراک RSS"
          }
        }
      }
    },
    "/api/options/x/parity": {
      "get": {
        "summary": "اسکنر نقض پاریتی خرید-فروش اختیار",
        "description": "جفت‌های کال/پوت هم‌سررسید و هم‌اعمال؛ انحراف از C−P=S−PV(K)، سمت کانورژن/ریورسال، بازده خالص سالانه. شکاف قیمت ثبت‌شده است نه آربیتراژ تضمینی.",
        "tags": [
          "options"
        ],
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "حداکثر ردیف",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "پاسخ موفق JSON"
          }
        }
      }
    },
    "/api/options/x/covered": {
      "get": {
        "summary": "تحلیل کاورد کال",
        "description": "بازده اگر اعمال شود/نشود (سالانه‌شده)، حاشیهٔ افت، سربه‌سر، سرمایهٔ هر لات و احتمال اعمالِ مدل‌محور برای هر اختیار خریدِ قیمت‌دار.",
        "tags": [
          "options"
        ],
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "حداکثر ردیف",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "پاسخ موفق JSON"
          }
        }
      }
    },
    "/api/options/x/strategy": {
      "get": {
        "summary": "پیشنهادگر استراتژی اختیار",
        "description": "از قراردادهای واقعیِ قیمت‌دار ترکیب (اسپرد/استرادل/کاوردکال…) با سود و زیان و سربه‌سر می‌سازد. توصیهٔ سرمایه‌گذاری نیست.",
        "tags": [
          "options"
        ],
        "parameters": [
          {
            "name": "underlying",
            "in": "query",
            "required": true,
            "description": "نماد پایه",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "view",
            "in": "query",
            "required": true,
            "description": "bullish|bearish|neutral|volatile",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "پاسخ موفق JSON"
          }
        }
      }
    },
    "/api/instflow/sectors": {
      "get": {
        "summary": "نقشهٔ ورود/خروج پول حقیقی صنایع",
        "description": "خالص خرید حقیقی هر صنعت در پنجره‌های ۵/۲۰/۶۰ نشست + ماتریس حرارتی روزانه + نمادهای پیشران/پس‌کش. از warmer پیش‌محاسبه می‌شود.",
        "tags": [
          "sector-lab"
        ],
        "responses": {
          "200": {
            "description": "پاسخ موفق JSON"
          }
        }
      }
    },
    "/api/instflow/chain": {
      "get": {
        "summary": "نقشهٔ زنجیرهٔ ارزش",
        "description": "ده زنجیرهٔ ارزش بورس تهران؛ بازده ۲۰ نشستهٔ هم‌وزن هر حلقه + خالص حقیقی ۲۰ روزه.",
        "tags": [
          "sector-lab"
        ],
        "responses": {
          "200": {
            "description": "پاسخ موفق JSON"
          }
        }
      }
    },
    "/api/quant-lab/weekday": {
      "get": {
        "summary": "آمار روزهای هفته",
        "description": "میانگین بازده و درصد روزهای مثبت به تفکیک شنبه تا چهارشنبه، برای نماد یا کل بازار.",
        "tags": [
          "quant-lab"
        ],
        "parameters": [
          {
            "name": "symbol",
            "in": "query",
            "required": false,
            "description": "نماد (خالی = کل بازار)",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "پاسخ موفق JSON"
          }
        }
      }
    },
    "/api/quant-lab/beta": {
      "get": {
        "summary": "رتبه‌بندی بتا",
        "description": "بتای نمادها نسبت به شاخص کل روی ۲۵۰ نشست اخیر؛ پیش‌محاسبه با warmer.",
        "tags": [
          "quant-lab"
        ],
        "responses": {
          "200": {
            "description": "پاسخ موفق JSON"
          }
        }
      }
    },
    "/api/sentiment": {
      "get": {
        "summary": "نتیجهٔ نظرسنجی گاو/خرس",
        "description": "آرای امروز (گاو/خرس/درصد) + سری ۳۰ روزهٔ نسبت گاو + رأی خود کاربر.",
        "tags": [
          "sentiment"
        ],
        "parameters": [
          {
            "name": "symbol",
            "in": "query",
            "required": false,
            "description": "نماد (خالی = کل بازار)",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "پاسخ موفق JSON"
          }
        }
      }
    },
    "/api/sentiment/vote": {
      "post": {
        "summary": "ثبت رأی گاو/خرس",
        "description": "یک رأی به‌ازای هر (نماد، رأی‌دهنده، روز)؛ body: {symbol?, vote:1|-1}. کاربر واردشده با شناسه، ناشناس با هش روزانهٔ سمت سرور.",
        "tags": [
          "sentiment"
        ],
        "responses": {
          "200": {
            "description": "پاسخ موفق JSON"
          }
        }
      }
    },
    "/api/user/digest": {
      "get": {
        "summary": "اشتراک خلاصه‌های ایمیلی",
        "description": "GET وضعیت اشتراک؛ POST ذخیرهٔ {daily,weekly}، پیش‌نمایش HTML و قالب وبهوک (json|telegram|bale). نیازمند ورود.",
        "tags": [
          "user"
        ],
        "responses": {
          "200": {
            "description": "پاسخ موفق JSON"
          }
        }
      }
    },
    "/api/strategy-market": {
      "get": {
        "summary": "بازار استراتژی — گالری",
        "description": "فهرست استراتژی‌های عمومی کاتالوگ + دسته‌ها.",
        "tags": [
          "strategy"
        ],
        "parameters": [
          {
            "name": "category",
            "in": "query",
            "required": false,
            "description": "فیلتر دسته",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "پاسخ موفق JSON"
          }
        }
      }
    },
    "/api/strategy-market/detail": {
      "get": {
        "summary": "بازار استراتژی — جزئیات",
        "description": "قواعد، حد ضرر/سود، دادهٔ موردنیاز و کارنامهٔ بک‌تست یک استراتژی.",
        "tags": [
          "strategy"
        ],
        "parameters": [
          {
            "name": "slug",
            "in": "query",
            "required": true,
            "description": "شناسهٔ استراتژی",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "پاسخ موفق JSON"
          }
        }
      }
    },
    "/api/strategy-market/clone": {
      "post": {
        "summary": "کلون استراتژی به حساب کاربر",
        "description": "کپی تعریف در user_strategy_builds؛ idempotent؛ نیازمند ورود.",
        "tags": [
          "strategy"
        ],
        "responses": {
          "200": {
            "description": "پاسخ موفق JSON"
          }
        }
      }
    },
    "/api/avwap": {
      "get": {
        "summary": "VWAP لنگردار",
        "description": "سری VWAP حجم‌وزنی از تاریخ لنگر تا امروز + لنگرهای پیشنهادی (سقف تاریخی، کف ۵۲هفته، آخرین تعدیل).",
        "tags": [
          "تحلیل تکنیکال"
        ],
        "parameters": [
          {
            "name": "symbol",
            "in": "query",
            "required": true,
            "description": "نماد",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "anchor",
            "in": "query",
            "required": false,
            "description": "تاریخ لنگر YYYYMMDD میلادی (خالی = خودکار)",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "پاسخ موفق JSON"
          }
        }
      }
    },
    "/api/breadth/ma": {
      "get": {
        "summary": "پهنای میانگین‌های متحرک",
        "description": "سری تاریخی درصد نمادهای بالای MA20/MA50/MA200.",
        "tags": [
          "بازار"
        ],
        "parameters": [
          {
            "name": "days",
            "in": "query",
            "required": false,
            "description": "طول بازه (روز)",
            "schema": {
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "پاسخ موفق JSON"
          }
        }
      }
    },
    "/api/gold-bubble": {
      "get": {
        "summary": "حباب‌سنج صندوق‌های طلا",
        "description": "قیمت تابلو نسبت به NAV و ارزش ذاتی تقریبی از طلای داخلی/جهانی برای صندوق‌های طلا.",
        "tags": [
          "صندوق‌ها"
        ],
        "parameters": [
          {
            "name": "calib_days",
            "in": "query",
            "required": false,
            "description": "بازهٔ کالیبراسیون نسبت (پیش‌فرض ۹۰)",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "سقف ردیف",
            "schema": {
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "پاسخ موفق JSON"
          }
        }
      }
    },
    "/api/gold-bubble/history": {
      "get": {
        "summary": "تاریخچهٔ حباب یک صندوق طلا",
        "description": "سری روزانهٔ نسبت قیمت صندوق به طلای ۱۸عیار و حباب نسبت به میانهٔ بازه.",
        "tags": [
          "صندوق‌ها"
        ],
        "parameters": [
          {
            "name": "symbol",
            "in": "query",
            "required": true,
            "description": "نماد صندوق",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "days",
            "in": "query",
            "required": false,
            "description": "طول بازه",
            "schema": {
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "پاسخ موفق JSON"
          }
        }
      }
    },
    "/api/sector-season": {
      "get": {
        "summary": "ماتریس بازده ماهانهٔ صنایع",
        "description": "میانگین/میانهٔ بازده هر صنعت در دوازده ماه شمسی طی چند سال.",
        "tags": [
          "بازار"
        ],
        "parameters": [
          {
            "name": "years",
            "in": "query",
            "required": false,
            "description": "تعداد سال (پیش‌فرض ۴)",
            "schema": {
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "پاسخ موفق JSON"
          }
        }
      }
    },
    "/api/quantx/custom-rank": {
      "get": {
        "summary": "غربال ترکیبی وزن‌دار",
        "description": "رتبهٔ ترکیبی شخصی از وزن‌دهی کاربر روی فاکتورهای کوانت.",
        "tags": [
          "quant-lab"
        ],
        "parameters": [
          {
            "name": "weights",
            "in": "query",
            "required": false,
            "description": "وزن فاکتورها (نام=۰..۱۰۰ با ویرگول)",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "پاسخ موفق JSON"
          }
        }
      }
    },
    "/api/wave/target-hits": {
      "get": {
        "summary": "ورود به نواحی هدف موج",
        "description": "نمادهایی که آخرین قیمتشان داخل ناحیهٔ هدف سناریوی برتر شمارش موج است.",
        "tags": [
          "موج"
        ],
        "parameters": [
          {
            "name": "min_conf",
            "in": "query",
            "required": false,
            "description": "حداقل اطمینان سناریو",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "سقف ردیف",
            "schema": {
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "پاسخ موفق JSON"
          }
        }
      }
    },
    "/api/fib-confluence": {
      "get": {
        "summary": "نواحی تلاقی فیبوناچی",
        "description": "باندهای پرتراکم از هم‌پوشانی سطوح بازگشتی/گسترشی پیوت‌های موج یک نماد.",
        "tags": [
          "موج"
        ],
        "parameters": [
          {
            "name": "symbol",
            "in": "query",
            "required": true,
            "description": "نماد",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "پاسخ موفق JSON"
          }
        }
      }
    },
    "/api/external/history": {
      "get": {
        "summary": "سری تاریخی بازار موازی",
        "description": "OHLC روزانهٔ یک قلم (usd/eur/coin_emami/coin_half/gold_18k/gold_ounce).",
        "tags": [
          "بازارها"
        ],
        "parameters": [
          {
            "name": "key",
            "in": "query",
            "required": true,
            "description": "کلید قلم",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "days",
            "in": "query",
            "required": false,
            "description": "طول بازه (تا ۱۵۰۰)",
            "schema": {
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "پاسخ موفق JSON"
          }
        }
      }
    },
    "/api/index/usd-adjusted": {
      "get": {
        "summary": "TEPIX دلاری",
        "description": "سری شاخص کل تقسیم بر نرخ روزانهٔ دلار.",
        "tags": [
          "بازار"
        ],
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "سقف ردیف",
            "schema": {
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "پاسخ موفق JSON"
          }
        }
      }
    },
    "/api/yield-curve": {
      "get": {
        "summary": "منحنی بازده اخزا",
        "description": "بازده تا سررسید اسناد خزانهٔ اسلامی از قیمت‌های تابلو.",
        "tags": [
          "بازارها"
        ],
        "parameters": [],
        "responses": {
          "200": {
            "description": "پاسخ موفق JSON"
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "Overview": {
        "type": "object",
        "properties": {
          "indexLast": {
            "type": "number",
            "description": "شاخص کل"
          },
          "indexEqualLast": {
            "type": "number",
            "description": "شاخص هم‌وزن"
          },
          "value": {
            "type": "number",
            "description": "ارزش کل معاملات (ریال)"
          },
          "marketState": {
            "type": "string",
            "description": "وضعیت بازار"
          }
        }
      },
      "Mover": {
        "type": "object",
        "properties": {
          "symbol": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "price_last": {
            "type": "number"
          },
          "change_pct": {
            "type": "number"
          },
          "volume": {
            "type": "number"
          },
          "value": {
            "type": "number"
          },
          "market": {
            "type": "string"
          }
        }
      },
      "ClientType": {
        "type": "object",
        "properties": {
          "NetRealValue": {
            "type": "number",
            "description": "خالص پول حقیقی"
          },
          "RealBuyValue": {
            "type": "number"
          },
          "RealSellValue": {
            "type": "number"
          },
          "LegalBuyValue": {
            "type": "number"
          },
          "LegalSellValue": {
            "type": "number"
          },
          "BuyCountI": {
            "type": "integer",
            "description": "شمار خریداران حقیقی"
          },
          "SellCountI": {
            "type": "integer",
            "description": "شمار فروشندگان حقیقی"
          },
          "BuyPower": {
            "type": "number",
            "description": "قدرت خریدار حقیقی (سرانه خرید÷سرانه فروش)"
          }
        }
      },
      "AuthCaptchaConfig": {
        "type": "object",
        "properties": {
          "enabled": {
            "type": "boolean",
            "description": "اگر true باشد، توکنِ Turnstile در request-otp الزامی است"
          },
          "sitekey": {
            "type": "string",
            "description": "کلیدِ عمومیِ ویجتِ Turnstile؛ وقتی enabled=false خالی است"
          },
          "channels": {
            "$ref": "#/components/schemas/AuthChannels"
          }
        },
        "required": [
          "enabled",
          "sitekey",
          "channels"
        ]
      },
      "AuthChannels": {
        "type": "object",
        "description": "کانالی که مقدارش false است روی سرور پیکربندی نشده و صفحهٔ ورود آن را پنهان می‌کند. کلیدها ثابت‌اند ولی additionalProperties باز مانده تا افزودنِ کانالِ تازه در سرور، مصرف‌کننده را نشکند.",
        "properties": {
          "phone": {
            "type": "boolean",
            "description": "پیامک (کاوه‌نگار)"
          },
          "email": {
            "type": "boolean",
            "description": "ایمیل (SMTP)"
          },
          "telegram": {
            "type": "boolean"
          },
          "bale": {
            "type": "boolean"
          },
          "google": {
            "type": "boolean",
            "description": "ورود با گوگل (OAuth)؛ کانالِ OTP نیست ولی همان قاعده را دارد: پیکربندی‌نشده ⇒ دکمه پنهان"
          }
        },
        "additionalProperties": {
          "type": "boolean"
        }
      },
      "FilterRequest": {
        "type": "object",
        "properties": {
          "market": {
            "type": "string",
            "enum": [
              "",
              "bourse",
              "farabourse",
              "tala",
              "sandogh",
              "oraagh"
            ]
          },
          "limit": {
            "type": "integer",
            "default": 50
          },
          "conds": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "field": {
                  "type": "string",
                  "enum": [
                    "price_last",
                    "change_pct",
                    "volume",
                    "value",
                    "trade_count",
                    "net_real",
                    "buy_power",
                    "pe"
                  ]
                },
                "op": {
                  "type": "string",
                  "enum": [
                    ">",
                    "<",
                    ">=",
                    "<="
                  ]
                },
                "value": {
                  "type": "number"
                }
              }
            }
          }
        }
      }
    }
  }
}
