{"openapi": "3.0.3", "info": {"title": "Planar Ultimate API", "description": "Public REST API for programmatic access to Planar data.\n\n**Authentication:** Bearer token (Ultimate plan only).\nGenerate your API token in **Settings \u2192 API** inside the Telegram Mini App.\n\n**Rate limit:** 300 requests/minute per token (sliding window).\n\n**Methods:** the API only reads. `GET` (and `HEAD`, answered like `GET`) is served, `OPTIONS` is the CORS preflight; any other method is refused with `405` (`METHOD_NOT_ALLOWED`, an `Allow` header).\n\n**Errors:** an error from the API is `{error, code}`. A request with no `Authorization: Bearer` header never reaches it: the CloudFront edge answers `401` with `{error, status, timestamp, protected_by, request_id}` and no `code`.", "version": "1.2.0", "contact": {"name": "Planar", "url": "https://planar.one"}}, "servers": [{"url": "https://api.planar.one", "description": "Production"}], "security": [{"BearerAuth": []}], "tags": [{"name": "Health", "description": "Health check (public)"}, {"name": "Symbols", "description": "Trading symbols and metadata"}, {"name": "Analysis", "description": "SMC market analysis"}, {"name": "Forecasts", "description": "Predictions and accuracy statistics"}, {"name": "Tokens", "description": "Usage of an API token"}], "paths": {"/v1/health": {"get": {"tags": ["Health"], "summary": "Health check", "description": "Returns API health status. **No authentication required.**", "operationId": "getHealth", "security": [], "responses": {"200": {"description": "API is healthy", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/Health"}}}}}}}, "/v1/symbols": {"get": {"tags": ["Symbols"], "summary": "List trading symbols", "description": "Returns all available trading symbols with metadata.", "operationId": "getSymbols", "parameters": [{"name": "active_only", "in": "query", "description": "Filter active symbols only", "schema": {"type": "string", "default": "true", "enum": ["true", "false"]}}], "responses": {"200": {"description": "List of symbols", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/SymbolList"}}}}, "401": {"$ref": "#/components/responses/Unauthorized"}, "403": {"$ref": "#/components/responses/PlanRequired"}, "429": {"$ref": "#/components/responses/RateLimited"}}}}, "/v1/symbols/{symbol}/status": {"get": {"tags": ["Symbols"], "summary": "Symbol analysis status", "description": "Get current analysis status for a symbol.", "operationId": "getSymbolStatus", "parameters": [{"$ref": "#/components/parameters/Symbol"}], "responses": {"200": {"description": "Symbol status", "content": {"application/json": {"schema": {"type": "object"}}}}, "401": {"$ref": "#/components/responses/Unauthorized"}, "403": {"$ref": "#/components/responses/PlanRequired"}, "404": {"$ref": "#/components/responses/NotFound"}, "429": {"$ref": "#/components/responses/RateLimited"}}}}, "/v1/token/usage": {"get": {"tags": ["Tokens"], "summary": "Token usage", "description": "How often an API token was used and when last. An API token reads its own usage (`tokenId` optional); a Mini App / web app JWT reads the usage of a token its user owns. Any other token reads as not found.", "operationId": "getTokenUsage", "parameters": [{"name": "tokenId", "in": "query", "description": "The token to read (required with a Mini App / web app JWT)", "schema": {"type": "string"}}], "responses": {"200": {"description": "Token usage", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/TokenUsage"}}}}, "400": {"$ref": "#/components/responses/BadRequest"}, "401": {"$ref": "#/components/responses/Unauthorized"}, "403": {"$ref": "#/components/responses/Forbidden"}, "404": {"$ref": "#/components/responses/NotFound"}}}}, "/v1/analysis/{symbol}/latest": {"get": {"tags": ["Analysis"], "summary": "Latest analysis for symbol", "description": "Get the latest SMC analysis for a trading symbol. A plan is saved as one analysis per leg (LONG at the lower edge, SHORT at the upper), so the body is the newest leg of the latest plan (the SHORT leg) unless `leg` asks for the other; `legs` lists every leg of that plan with its own `analysis_id` and levels.", "operationId": "getAnalysisLatest", "parameters": [{"$ref": "#/components/parameters/Symbol"}, {"name": "leg", "in": "query", "description": "The leg to return as the body (default: the newest leg of the latest plan)", "schema": {"type": "string", "enum": ["LONG", "SHORT"]}}], "responses": {"200": {"description": "Full analysis", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/Analysis"}}}}, "400": {"$ref": "#/components/responses/BadRequest"}, "401": {"$ref": "#/components/responses/Unauthorized"}, "403": {"$ref": "#/components/responses/PlanRequired"}, "404": {"$ref": "#/components/responses/NotFound"}, "429": {"$ref": "#/components/responses/RateLimited"}}}}, "/v1/analysis/{analysisId}": {"get": {"tags": ["Analysis"], "summary": "Analysis by ID", "description": "Get a specific analysis by its unique identifier.", "operationId": "getAnalysisById", "parameters": [{"$ref": "#/components/parameters/AnalysisId"}], "responses": {"200": {"description": "Full analysis", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/Analysis"}}}}, "401": {"$ref": "#/components/responses/Unauthorized"}, "403": {"$ref": "#/components/responses/PlanRequired"}, "404": {"$ref": "#/components/responses/NotFound"}, "429": {"$ref": "#/components/responses/RateLimited"}}}}, "/v1/analysis/{symbol}/history": {"get": {"tags": ["Analysis"], "summary": "Analysis history", "description": "Get analysis history for a symbol (newest first). Every row is one leg of a plan (`leg`, `plan_id`) with its levels, so a plan takes two rows.", "operationId": "getAnalysisHistory", "parameters": [{"$ref": "#/components/parameters/Symbol"}, {"name": "limit", "in": "query", "description": "Number of results (max 50)", "schema": {"type": "integer", "default": 10, "minimum": 1, "maximum": 50}}], "responses": {"200": {"description": "Analysis history", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/AnalysisHistory"}}}}, "400": {"$ref": "#/components/responses/BadRequest"}, "401": {"$ref": "#/components/responses/Unauthorized"}, "403": {"$ref": "#/components/responses/PlanRequired"}, "429": {"$ref": "#/components/responses/RateLimited"}}}}, "/v1/analysis/{analysisId}/levels": {"get": {"tags": ["Analysis"], "summary": "Trading levels", "description": "Get trading levels (entry zones, stop-loss, take-profit) from an analysis. Lightweight endpoint for automated order placement.", "operationId": "getAnalysisLevels", "parameters": [{"$ref": "#/components/parameters/AnalysisId"}], "responses": {"200": {"description": "Trading levels", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/AnalysisLevels"}}}}, "401": {"$ref": "#/components/responses/Unauthorized"}, "403": {"$ref": "#/components/responses/PlanRequired"}, "404": {"$ref": "#/components/responses/NotFound"}, "429": {"$ref": "#/components/responses/RateLimited"}}}}, "/v1/analysis/{analysisId}/scenario": {"get": {"tags": ["Analysis"], "summary": "Trading scenario", "description": "Get the structured trading scenario: general info, market dynamics, risk factors, recommended approach, and trading setups with entry/SL/TP levels.", "operationId": "getAnalysisScenario", "parameters": [{"$ref": "#/components/parameters/AnalysisId"}], "responses": {"200": {"description": "Trading scenario", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/AnalysisScenario"}}}}, "401": {"$ref": "#/components/responses/Unauthorized"}, "403": {"$ref": "#/components/responses/PlanRequired"}, "404": {"$ref": "#/components/responses/NotFound"}, "429": {"$ref": "#/components/responses/RateLimited"}}}}, "/v1/forecasts/active": {"get": {"tags": ["Forecasts"], "summary": "Active predictions", "description": "Get all active predictions across all symbols.", "operationId": "getForecastsActive", "parameters": [{"name": "limit", "in": "query", "description": "Number of results (max 500)", "schema": {"type": "integer", "default": 100, "minimum": 1, "maximum": 500}}], "responses": {"200": {"description": "Active predictions", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ForecastsActive"}}}}, "400": {"$ref": "#/components/responses/BadRequest"}, "401": {"$ref": "#/components/responses/Unauthorized"}, "403": {"$ref": "#/components/responses/PlanRequired"}, "429": {"$ref": "#/components/responses/RateLimited"}}}}, "/v1/forecasts/active/{symbol}": {"get": {"tags": ["Forecasts"], "summary": "Active predictions by symbol", "description": "Get active predictions for a specific symbol (newest first).", "operationId": "getForecastsActiveSymbol", "parameters": [{"$ref": "#/components/parameters/Symbol"}, {"name": "limit", "in": "query", "description": "Number of results (max 200)", "schema": {"type": "integer", "default": 50, "minimum": 1, "maximum": 200}}], "responses": {"200": {"description": "Active predictions for symbol", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ForecastsActiveSymbol"}}}}, "400": {"$ref": "#/components/responses/BadRequest"}, "401": {"$ref": "#/components/responses/Unauthorized"}, "403": {"$ref": "#/components/responses/PlanRequired"}, "429": {"$ref": "#/components/responses/RateLimited"}}}}, "/v1/forecasts/statistics": {"get": {"tags": ["Forecasts"], "summary": "Forecast statistics", "description": "Get forecast accuracy statistics, one record per day, newest first. Defaults to the `daily` period.", "operationId": "getForecastsStatistics", "parameters": [{"name": "period", "in": "query", "description": "Statistics period", "schema": {"type": "string", "default": "daily", "enum": ["daily", "weekly", "monthly"]}}, {"$ref": "#/components/parameters/StatisticsLimit"}], "responses": {"200": {"description": "Forecast statistics", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ForecastsStatistics"}}}}, "400": {"$ref": "#/components/responses/BadRequest"}, "401": {"$ref": "#/components/responses/Unauthorized"}, "403": {"$ref": "#/components/responses/PlanRequired"}, "429": {"$ref": "#/components/responses/RateLimited"}}}}, "/v1/forecasts/statistics/{period}": {"get": {"tags": ["Forecasts"], "summary": "Forecast statistics by period", "description": "Get forecast accuracy statistics for a specific period, one record per day, newest first.", "operationId": "getForecastsStatisticsPeriod", "parameters": [{"name": "period", "in": "path", "required": true, "description": "Statistics period", "schema": {"type": "string", "enum": ["daily", "weekly", "monthly"]}}, {"$ref": "#/components/parameters/StatisticsLimit"}], "responses": {"200": {"description": "Forecast statistics", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ForecastsStatistics"}}}}, "400": {"$ref": "#/components/responses/BadRequest"}, "401": {"$ref": "#/components/responses/Unauthorized"}, "403": {"$ref": "#/components/responses/PlanRequired"}, "429": {"$ref": "#/components/responses/RateLimited"}}}}}, "components": {"securitySchemes": {"BearerAuth": {"type": "http", "scheme": "bearer", "bearerFormat": "JWT", "description": "API token generated in Settings \u2192 API (Ultimate plan required). JWT signed with ES256, scope `api_v1`."}}, "parameters": {"Symbol": {"name": "symbol", "in": "path", "required": true, "description": "Trading pair (e.g. BTCUSDT). Case-insensitive.", "schema": {"type": "string", "example": "BTCUSDT"}}, "StatisticsLimit": {"name": "limit", "in": "query", "description": "Number of days (max 366)", "schema": {"type": "integer", "default": 30, "minimum": 1, "maximum": 366}}, "AnalysisId": {"name": "analysisId", "in": "path", "required": true, "description": "Unique analysis identifier (e.g. BTCUSDT_2026-02-10T08:00:00Z)", "schema": {"type": "string", "example": "BTCUSDT_2026-02-10T08:00:00Z"}}}, "responses": {"Unauthorized": {"description": "Authentication failed", "content": {"application/json": {"schema": {"anyOf": [{"$ref": "#/components/schemas/Error"}, {"$ref": "#/components/schemas/EdgeError"}]}, "examples": {"no_header": {"summary": "No Authorization: Bearer header (answered by the CloudFront edge: no code)", "value": {"error": "Authorization Bearer token required", "status": 401, "timestamp": "2026-02-10T12:00:00.000Z", "protected_by": "cloudfront_functions", "request_id": "a1b2c3d4"}}, "invalid_token": {"summary": "Not a valid token (signature, issuer, expiry)", "value": {"error": "Invalid or expired token", "code": "INVALID_TOKEN"}}, "invalid_scope": {"summary": "A valid JWT that is not an API token (the Mini App's own, for one)", "value": {"error": "Invalid token scope for API access", "code": "INVALID_SCOPE"}}, "revoked": {"summary": "Revoked, or the subscription expired", "value": {"error": "Token has been revoked or subscription expired", "code": "TOKEN_REVOKED"}}}}}}, "PlanRequired": {"description": "The token is valid but its plan is not Ultimate", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/Error"}, "example": {"error": "API access requires Ultimate subscription", "code": "PLAN_REQUIRED"}}}}, "BadRequest": {"description": "Invalid parameter (limit, period, leg)", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/Error"}, "example": {"error": "limit must be at least 1", "code": "INVALID_PARAMETER"}}}}, "Forbidden": {"description": "Not allowed to read this resource", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/Error"}, "example": {"error": "An API token can read only its own usage", "code": "FORBIDDEN"}}}}, "NotFound": {"description": "Resource not found", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/Error"}, "example": {"error": "Symbol not found: FAKESYMBOL", "code": "NOT_FOUND"}}}}, "RateLimited": {"description": "Rate limit exceeded (300 req/min)", "content": {"application/json": {"schema": {"type": "object", "properties": {"error": {"type": "string"}, "code": {"type": "string", "example": "RATE_LIMIT_EXCEEDED"}, "rate_limit": {"type": "integer", "example": 300}, "remaining": {"type": "integer", "example": 0}, "reset_at": {"type": "integer", "example": 1707523200}}}}}}}, "schemas": {"Error": {"type": "object", "properties": {"error": {"type": "string", "description": "Human-readable error message"}, "code": {"type": "string", "description": "Machine-readable error code"}}, "required": ["error", "code"]}, "EdgeError": {"type": "object", "description": "The CloudFront edge's refusal of a request with no Authorization: Bearer header", "properties": {"error": {"type": "string", "example": "Authorization Bearer token required"}, "status": {"type": "integer", "example": 401}, "timestamp": {"type": "string", "format": "date-time"}, "protected_by": {"type": "string", "example": "cloudfront_functions"}, "request_id": {"type": "string"}}, "required": ["error"]}, "TokenUsage": {"type": "object", "properties": {"usageCount": {"type": "integer", "example": 120}, "lastUsedAt": {"type": "string", "format": "date-time", "nullable": true}}}, "Health": {"type": "object", "properties": {"status": {"type": "string", "example": "healthy"}, "api": {"type": "string", "example": "ultimate"}, "version": {"type": "string", "example": "1.2.0"}, "timestamp": {"type": "string", "format": "date-time", "example": "2026-02-10T12:00:00.000000Z"}}}, "SymbolList": {"type": "object", "properties": {"symbols": {"type": "array", "items": {"type": "string"}, "example": ["BTCUSDT", "ETHUSDT", "SOLUSDT"]}, "count": {"type": "integer", "example": 15}}}, "TradingScenario": {"type": "object", "description": "One leg of a range plan: the entry zone, entry, stop and targets are the plan's own levels; market_context and smc_trigger come from the plan's narrative", "properties": {"type": {"type": "string", "enum": ["LONG", "SHORT"], "description": "Trade direction"}, "entry": {"type": "number", "example": 0.9738, "description": "The plan's entry: a limit leg's limit, a sweep leg's ARM close"}, "entry_range": {"type": "object", "properties": {"optimal": {"type": "number", "example": 0.9738, "description": "Same as `entry`"}, "min": {"type": "number", "example": 0.9665}, "max": {"type": "number", "example": 0.9856}}}, "stop_loss": {"type": "object", "properties": {"level": {"type": "number", "example": 1.0268}, "structure_level": {"type": "number", "nullable": true, "deprecated": true, "description": "Deprecated: not produced (legs saved before carry it)"}, "basis": {"type": "string", "nullable": true, "deprecated": true, "description": "Deprecated: not produced (legs saved before carry it)"}, "liquidity_zone": {"type": "number", "nullable": true, "deprecated": true, "description": "Deprecated: not produced for range-plan legs"}}}, "targets": {"type": "array", "description": "The plan's take-profit targets, in order", "items": {"type": "object", "properties": {"level": {"type": "number", "example": 0.895}, "name": {"type": "string", "example": "T1"}, "note": {"type": "string", "example": "4h swing low"}, "probability": {"type": "number", "nullable": true, "deprecated": true, "description": "Always null: a target is the plan's, not estimated"}, "structure_type": {"type": "string", "nullable": true, "deprecated": true, "description": "Deprecated: not produced for range-plan legs"}, "distance_pct": {"type": "number", "nullable": true, "deprecated": true, "description": "Deprecated: not produced for range-plan legs"}, "basis": {"type": "string", "nullable": true, "deprecated": true, "description": "Deprecated: not produced (legs saved before carry it)"}}}}, "probability": {"type": "number", "nullable": true, "deprecated": true, "description": "Always null: a leg is the plan's, not estimated"}, "priority": {"type": "string", "enum": ["LOW", "MEDIUM", "HIGH", "VERY_HIGH"], "nullable": true, "deprecated": true, "description": "Deprecated: not produced for range-plan legs"}, "risk_reward": {"type": "number", "example": 1.49, "nullable": true, "description": "Entry \u2192 TP1 over entry \u2192 stop (a sweep leg: its ARM close to its stop, sl_far)"}, "timeframe_basis": {"type": "string", "example": "4h"}, "trigger_level": {"type": "number", "nullable": true, "deprecated": true, "description": "Deprecated: not produced for range-plan legs (see plan.trigger / plan.entry)"}, "trading_management": {"type": "object", "description": "pre_entry_validation.scenario_invalidation: the leg's invalid_close (a candle close beyond it before entry cancels the leg)"}, "reference_levels": {"type": "array", "description": "The plan's annotated context levels (chart display only)", "items": {"type": "object", "properties": {"name": {"type": "string"}, "price": {"type": "number"}, "note": {"type": "string"}}}}, "mid_target": {"type": "object", "nullable": true, "description": "Optional intermediate target between T1 and T2 (absent when the leg has no mid)", "properties": {"level": {"type": "number", "example": 1.12}, "name": {"type": "string", "example": "Mid"}, "note": {"type": "string", "nullable": true}, "basis": {"type": "string", "nullable": true, "enum": ["4h_break", "1d_break", "1d_eq", "1d_high", "1d_low"], "description": "The forecast level the mid was chosen from"}}}, "market_context": {"type": "string", "description": "Narrative: why the leg sits where it does"}, "smc_trigger": {"type": "string", "description": "Narrative: the leg's entry rule in SMC terms"}, "distance_from_current": {"type": "number", "nullable": true, "deprecated": true, "description": "Always null: compare `entry` with a live price"}}}, "RangePlan": {"type": "object", "description": "The range plan this analysis is one leg of: a LONG leg at the lower edge and/or a SHORT leg at the upper edge, each its own analysis, tied by plan_id. Every leg is tracked the same way and on its own: both can be in a trade at once.", "properties": {"plan_id": {"type": "string", "example": "btc_range_20260928_0300"}, "plan_type": {"type": "string", "enum": ["range"]}, "leg": {"type": "string", "enum": ["LONG", "SHORT"], "description": "This analysis' leg"}, "legs": {"type": "array", "items": {"type": "string", "enum": ["LONG", "SHORT"]}}, "verdict": {"type": "string", "enum": ["READY", "ARMED", "WATCH"]}, "entry_mode": {"type": "string", "enum": ["sweep_reclaim", "limit"], "description": "sweep_reclaim: stage 1 at trigger, entry at the close of a timeframe candle back beyond arm_close"}, "timeframe": {"type": "string", "example": "4h", "description": "ARM timeframe"}, "plan_start_utc": {"type": "string", "format": "date-time"}, "range": {"type": "object", "properties": {"low": {"type": "number"}, "high": {"type": "number"}}}, "deep_zone": {"type": "object", "properties": {"top": {"type": "number"}, "bottom": {"type": "number"}}}, "trigger": {"type": "number", "example": 82282.8}, "arm_close": {"type": "number", "example": 82832.0}, "arm_mode": {"type": "string", "enum": ["reclaim"]}, "sweep_max": {"type": "number", "description": "A sweep beyond it cancels the leg"}, "sl_buffer_pct": {"type": "number", "example": 0.25, "description": "The buffer sl_far sits beyond sweep_max, %"}, "sl_far": {"type": "number", "description": "The leg's stop: sweep_max beyond by sl_buffer_pct, from the save to the exit"}, "entry": {"type": "number", "description": "limit legs: entry price"}}}, "Analysis": {"type": "object", "description": "Full SMC market analysis", "properties": {"analysis_id": {"type": "string", "example": "BTCUSDT_2026-02-06T21:32:58.048343"}, "symbol": {"type": "string", "example": "BTCUSDT"}, "timestamp": {"type": "string", "format": "date-time"}, "price": {"type": "number", "example": 97500.0, "description": "Price when the plan was saved, not the current price"}, "bias": {"type": "string", "enum": ["BULLISH", "BEARISH", "NEUTRAL"]}, "confidence": {"type": "number", "nullable": true, "example": 0.85, "description": "How well the structure supports the leg (0-1), from the plan's narrative"}, "structure": {"type": "string", "example": "VERY_STRONG", "description": "Market structure strength"}, "smc_data": {"type": "object", "nullable": true, "additionalProperties": {"$ref": "#/components/schemas/SmcStructures"}, "description": "SMC structures of the analysis's timeframes (its own and 1h / 4h / 1d), computed on request, keyed by timeframe; null when none could be computed. Each timeframe has fair_value_gaps {gaps, count}, order_blocks {blocks, count}, breaker_blocks {blocks, count}, bos_choch, swing_points, liquidity_zones and summary; rows oldest first: the last 30 gaps, 20 order blocks and 15 breakers; count = every one found (breakers: every one that still holds). A gap is in play until it is filled, an order block until it is violated; a touched one still is. Zones in play older than the listed rows are left out. A breaker's type is the side it acts on now (bullish support, bearish resistance), original_type the failed order block's side; status Active or Converted; a breaker that failed too is not listed."}, "trading_scenarios": {"type": "array", "items": {"$ref": "#/components/schemas/TradingScenario"}}, "market_dynamics": {"type": "string", "description": "Market dynamics narrative"}, "risk_factors": {"type": "array", "items": {"type": "string"}, "description": "Risk factors identified in the analysis"}, "recommended_approach": {"type": "object", "description": "AI-recommended trading approach"}, "plan": {"$ref": "#/components/schemas/RangePlan"}, "legs": {"type": "array", "description": "Only on /analysis/{symbol}/latest: every leg of the latest plan, newest first", "items": {"$ref": "#/components/schemas/AnalysisLeg"}}}}, "SmcStructures": {"type": "object", "description": "The SMC structures computed from one timeframe's latest candles (swing length 50)", "properties": {"timeframe": {"type": "string", "example": "4h"}, "candles_count": {"type": "integer", "minimum": 100, "description": "Candles the structures were computed from"}, "current_price": {"type": "number", "description": "The last close"}, "from_cache": {"type": "boolean", "description": "Whether the candles came from the cache"}, "fair_value_gaps": {"type": "object", "properties": {"gaps": {"type": "array", "maxItems": 30, "items": {"$ref": "#/components/schemas/SmcFairValueGap"}}, "count": {"type": "integer", "description": "Every gap found, spent ones included"}}, "required": ["gaps", "count"]}, "order_blocks": {"type": "object", "properties": {"blocks": {"type": "array", "maxItems": 20, "items": {"$ref": "#/components/schemas/SmcOrderBlock"}}, "count": {"type": "integer", "description": "Every order block found, spent ones included"}}, "required": ["blocks", "count"]}, "breaker_blocks": {"type": "object", "properties": {"blocks": {"type": "array", "maxItems": 15, "items": {"$ref": "#/components/schemas/SmcBreakerBlock"}}, "count": {"type": "integer", "description": "Every breaker that still holds"}}, "required": ["blocks", "count"]}, "bos_choch": {"type": "object", "properties": {"events": {"type": "array", "maxItems": 30, "items": {"$ref": "#/components/schemas/SmcStructureBreak"}}, "bos_count": {"type": "integer"}, "choch_count": {"type": "integer"}}, "required": ["events", "bos_count", "choch_count"]}, "swing_points": {"type": "object", "properties": {"points": {"type": "array", "maxItems": 50, "items": {"$ref": "#/components/schemas/SmcSwingPoint"}}, "count": {"type": "integer"}}, "required": ["points", "count"]}, "liquidity_zones": {"type": "object", "properties": {"zones": {"type": "array", "maxItems": 20, "items": {"$ref": "#/components/schemas/SmcLiquidityZone"}}, "count": {"type": "integer"}}, "required": ["zones", "count"]}, "summary": {"type": "object", "description": "Each group's count", "properties": {"fvg_count": {"type": "integer"}, "ob_count": {"type": "integer"}, "bos_count": {"type": "integer"}, "choch_count": {"type": "integer"}, "swing_count": {"type": "integer"}, "liquidity_count": {"type": "integer"}, "breaker_count": {"type": "integer"}}, "required": ["fvg_count", "ob_count", "bos_count", "choch_count", "swing_count", "liquidity_count", "breaker_count"]}}, "required": ["timeframe", "candles_count", "current_price", "from_cache", "fair_value_gaps", "order_blocks", "breaker_blocks", "bos_choch", "swing_points", "liquidity_zones", "summary"]}, "SmcFairValueGap": {"type": "object", "properties": {"index": {"type": "integer", "description": "The candle's position in this candle set (changes as candles come in)"}, "time": {"type": "string", "description": "The candle's open time, ISO 8601 UTC, no offset", "example": "2026-09-18T00:00:00"}, "type": {"type": "string", "enum": ["bullish", "bearish"]}, "top": {"type": "number"}, "bottom": {"type": "number"}, "gap_percent": {"type": "number", "description": "The gap's size as a percentage of its middle price"}, "touched": {"type": "boolean", "description": "Price reached the near edge: partially filled, still in play"}, "filled": {"type": "boolean", "description": "Price reached the far edge: no longer in play"}, "mitigated": {"type": "boolean", "description": "Alias of touched"}, "fully_mitigated": {"type": "boolean", "description": "Alias of filled"}, "mitigated_at_index": {"type": "integer", "description": "Present when touched"}, "mitigated_at_time": {"type": "string", "description": "Present when touched"}, "fully_mitigated_at_index": {"type": "integer", "description": "Present when filled"}, "fully_mitigated_at_time": {"type": "string", "description": "Present when filled"}}, "required": ["index", "time", "type", "top", "bottom", "touched", "filled", "mitigated", "fully_mitigated"]}, "SmcOrderBlock": {"type": "object", "properties": {"index": {"type": "integer"}, "time": {"type": "string"}, "type": {"type": "string", "enum": ["bullish", "bearish"]}, "top": {"type": "number"}, "bottom": {"type": "number"}, "strength": {"type": "number", "minimum": 0, "maximum": 1}, "touched": {"type": "boolean", "description": "Price came back into the zone after the breakout: tapped, still in play"}, "violated": {"type": "boolean", "description": "Price traded through the far side: no longer in play"}, "mitigated": {"type": "boolean", "description": "Alias of violated"}, "touched_at_index": {"type": "integer", "description": "Present when touched"}, "touched_at_time": {"type": "string", "description": "Present when touched"}, "mitigated_at_index": {"type": "integer", "description": "Present when violated"}, "mitigated_at_time": {"type": "string", "description": "Present when violated"}}, "required": ["index", "time", "type", "top", "bottom", "touched", "violated", "mitigated"]}, "SmcBreakerBlock": {"type": "object", "description": "An order block that failed and now acts the other way; `index` / `time` are the original block's candle", "properties": {"index": {"type": "integer"}, "time": {"type": "string"}, "type": {"type": "string", "enum": ["bullish", "bearish"], "description": "The side the zone acts on now"}, "original_type": {"type": "string", "enum": ["bullish", "bearish"], "description": "The failed order block's own side"}, "top": {"type": "number"}, "bottom": {"type": "number"}, "status": {"type": "string", "enum": ["Active", "Converted"]}, "retest_count": {"type": "integer", "minimum": 0}, "strength": {"type": "number"}}, "required": ["index", "time", "type", "original_type", "top", "bottom", "status", "retest_count"]}, "SmcStructureBreak": {"type": "object", "properties": {"index": {"type": "integer"}, "time": {"type": "string"}, "type": {"type": "string", "enum": ["bos", "choch"]}, "direction": {"type": "string", "enum": ["bullish", "bearish"]}, "level": {"type": "number"}, "broken_index": {"type": "integer"}}, "required": ["index", "time", "type", "direction"]}, "SmcSwingPoint": {"type": "object", "properties": {"index": {"type": "integer"}, "time": {"type": "string"}, "type": {"type": "string", "enum": ["high", "low"]}, "level": {"type": "number"}}, "required": ["index", "time", "type", "level"]}, "SmcLiquidityZone": {"type": "object", "properties": {"index": {"type": "integer"}, "time": {"type": "string"}, "type": {"type": "string", "enum": ["buy_side", "sell_side"]}, "level": {"type": "number"}, "swept": {"type": "boolean"}, "end_index": {"type": "integer"}, "end_time": {"type": "string"}, "swept_at_index": {"type": "integer", "description": "Present when swept"}, "swept_at_time": {"type": "string", "description": "Present when swept"}}, "required": ["index", "time", "type", "level", "swept"]}, "AnalysisLeg": {"type": "object", "description": "One leg of a plan: its analysis id and levels (no SMC structures)", "properties": {"leg": {"type": "string", "enum": ["LONG", "SHORT"]}, "analysis_id": {"type": "string", "example": "BTCUSDT_2026-02-06T21:32:58.048343"}, "timestamp": {"type": "string", "format": "date-time"}, "trading_scenarios": {"type": "array", "items": {"$ref": "#/components/schemas/TradingScenario"}}, "plan": {"$ref": "#/components/schemas/RangePlan"}}}, "AnalysisSummary": {"type": "object", "description": "Lightweight analysis summary", "properties": {"analysis_id": {"type": "string", "example": "BTCUSDT_2026-02-06T21:32:58.048343"}, "symbol": {"type": "string", "example": "BTCUSDT"}, "timestamp": {"type": "string", "format": "date-time"}, "price": {"type": "number", "example": 97500.0}, "bias": {"type": "string", "enum": ["BULLISH", "BEARISH", "NEUTRAL"]}, "confidence": {"type": "number", "nullable": true, "example": 0.85, "description": "How well the structure supports the leg (0-1), from the plan's narrative"}, "leg": {"type": "string", "nullable": true, "enum": ["LONG", "SHORT"]}, "plan_id": {"type": "string", "nullable": true, "description": "Shared by the legs of a plan"}, "entry": {"type": "number", "nullable": true}, "stop_loss": {"type": "number", "nullable": true}, "targets": {"type": "array", "items": {"type": "number"}, "description": "Target levels in order"}, "mid_target": {"type": "number", "nullable": true, "description": "Optional intermediate target level between T1 and T2"}, "risk_reward": {"type": "number", "nullable": true, "description": "Entry to the first target against entry to the stop"}}}, "AnalysisHistory": {"type": "object", "properties": {"symbol": {"type": "string", "example": "BTCUSDT"}, "analyses": {"type": "array", "items": {"$ref": "#/components/schemas/AnalysisSummary"}}, "count": {"type": "integer", "example": 10}}}, "AnalysisLevels": {"type": "object", "description": "Trading levels extracted from analysis", "properties": {"analysis_id": {"type": "string", "example": "BTCUSDT_2026-02-10T08:00:00Z"}, "trading_scenarios": {"type": "array", "items": {"$ref": "#/components/schemas/TradingScenario"}}, "count": {"type": "integer", "example": 1}, "plan": {"$ref": "#/components/schemas/RangePlan"}}}, "AnalysisScenario": {"type": "object", "description": "Structured trading scenario with full context", "properties": {"analysis_id": {"type": "string", "example": "BTCUSDT_2026-02-06T21:32:58.048343"}, "symbol": {"type": "string", "example": "BTCUSDT"}, "timestamp": {"type": "string", "format": "date-time"}, "general": {"type": "object", "properties": {"price": {"type": "number", "example": 97500.0}, "bias": {"type": "string", "enum": ["BULLISH", "BEARISH", "NEUTRAL"]}, "confidence": {"type": "number", "nullable": true, "example": 0.85, "description": "How well the structure supports the leg (0-1), from the plan's narrative"}, "structure": {"type": "string", "example": "VERY_STRONG"}}}, "market_dynamics": {"type": "string", "description": "Market dynamics narrative"}, "risk_factors": {"type": "array", "items": {"type": "string"}, "description": "Risk factors identified in the analysis"}, "recommended_approach": {"type": "object", "description": "AI-recommended trading approach"}, "trading_scenarios": {"type": "array", "items": {"$ref": "#/components/schemas/TradingScenario"}}, "plan": {"$ref": "#/components/schemas/RangePlan"}}}, "ForecastsActive": {"type": "object", "properties": {"predictions": {"type": "array", "items": {"$ref": "#/components/schemas/Prediction"}}, "count": {"type": "integer", "example": 5}}}, "ForecastsActiveSymbol": {"type": "object", "properties": {"symbol": {"type": "string", "example": "BTCUSDT"}, "predictions": {"type": "array", "items": {"$ref": "#/components/schemas/Prediction"}}, "count": {"type": "integer", "example": 0}}}, "Prediction": {"type": "object", "properties": {"prediction_id": {"type": "string", "example": "BTCUSDT_2026-02-06T21:32:58.048343", "description": "The leg's analysis_id: its levels are at /analysis/{prediction_id}/levels"}, "symbol": {"type": "string", "example": "BTCUSDT"}, "outcome_status": {"type": "string", "enum": ["WAITING_FOR_ENTRY", "IN_PROGRESS", "TP_HIT_PARTIAL", "TP_HIT_MID", "TP_HIT", "SL_HIT", "INVALIDATED", "SL_HIT_INVALIDATED", "TP_HIT_INVALIDATED", "TP_HIT_PARTIAL_SL", "TP_HIT_MID_SL"], "example": "WAITING_FOR_ENTRY"}, "status_category": {"type": "string", "enum": ["ACTIVE", "RESOLVED"], "example": "ACTIVE"}, "analysis_timestamp": {"type": "string", "format": "date-time"}, "created_at": {"type": "string", "format": "date-time"}, "last_updated": {"type": "string", "format": "date-time"}, "last_price_check": {"type": "string", "format": "date-time", "description": "When price-checker last read the leg"}, "range_plan": {"type": "object", "description": "The plan this prediction is one leg of", "properties": {"plan_id": {"type": "string"}, "leg": {"type": "string", "enum": ["LONG", "SHORT"]}}}, "entry_price": {"type": "number", "description": "Only once recorded: where the leg entered"}, "entry_zones": {"type": "array", "items": {"type": "object"}, "description": "Only when recorded"}, "tp_levels": {"type": "array", "items": {"type": "object"}, "description": "Only when recorded"}, "sl_level": {"type": "number", "description": "Only when recorded"}, "symbol_prediction_id": {"type": "string", "example": "BTCUSDT#BTCUSDT_2026-02-06T21:32:58.048343"}}}, "ForecastStatistic": {"type": "object", "description": "Forecast accuracy statistics for a time window", "properties": {"statistic_type": {"type": "string", "example": "forecast_accuracy_daily"}, "calculation_timestamp": {"type": "string", "format": "date-time"}, "resolved_predictions": {"type": "integer", "description": "Predictions in a terminal state, invalidated ones included"}, "traded_predictions": {"type": "integer", "description": "Entered trades: the sample of win_rate"}, "window_type": {"type": "string", "enum": ["daily", "weekly", "monthly"]}, "calculation_date": {"type": "string", "example": "2026-02-01"}, "time_period": {"type": "string", "example": "2026-02-01"}, "total_predictions": {"type": "integer", "example": 18}, "total_scenarios": {"type": "integer", "example": 18}, "accuracy_metrics": {"type": "object", "properties": {"win_rate": {"type": "number"}, "tp_success_rate": {"type": "number"}, "sl_hit_rate": {"type": "number"}, "entry_accuracy": {"type": "number"}, "loss_rate": {"type": "number"}, "win_rate_sample_size": {"type": "integer", "description": "Trades behind win_rate"}, "win_rate_ci_low": {"type": "number", "description": "95% Wilson lower bound of win_rate (%)"}, "win_rate_ci_high": {"type": "number", "description": "95% Wilson upper bound of win_rate (%)"}}}, "outcomes": {"type": "object", "properties": {"tp_hit": {"type": "integer"}, "sl_hit": {"type": "integer"}, "tp_hit_partial": {"type": "integer"}, "invalidated": {"type": "integer"}, "waiting_for_entry": {"type": "integer"}, "in_progress": {"type": "integer"}}}, "risk_reward_metrics": {"type": "object", "properties": {"win_loss_ratio": {"type": "number"}, "expectancy": {"type": "number"}}}, "window_start": {"type": "string", "format": "date-time"}, "window_end": {"type": "string", "format": "date-time"}, "window_days": {"type": "integer", "example": 1}, "window_description": {"type": "string", "example": "Last 24 hours"}}}, "ForecastsStatistics": {"type": "object", "properties": {"period": {"type": "string", "enum": ["daily", "weekly", "monthly"]}, "statistics": {"type": "array", "items": {"$ref": "#/components/schemas/ForecastStatistic"}}, "count": {"type": "integer", "example": 13, "description": "Days returned (newest first)"}}}}}}