{
  "openapi": "3.1.0",
  "info": {
    "title": "Tradewink REST API",
    "version": "1.0.0",
    "description": "Programmatic access to Tradewink's AI-powered stock analysis, portfolio tracking, strategies, and trading intelligence.\n\n## Authentication\n\nAll `/v1/` endpoints require a valid API key passed via the `Authorization` header:\n\n```\nAuthorization: Bearer tw_<your_api_key>\n```\n\nGenerate a key via the `/apikey generate` Discord command or `POST /v1/api-keys`.\n\n## Permission Scopes\n\n| Scope | Access |\n|-------|--------|\n| `read` | GET endpoints \u2014 watchlist, portfolio, quotes, strategies, alerts |\n| `trade` | `read` + POST/DELETE \u2014 watchlist mutations, API key management |\n| `admin` | `trade` + agent control, configuration |\n\n## Rate Limits\n\nPer-key rate limits are enforced over a 60-second sliding window (default: 120 req/min for read, 60 for trade, 30 for admin). Every authenticated `/v1/` response carries `X-RateLimit-Limit`, `X-RateLimit-Remaining` and `X-RateLimit-Reset` (seconds until the oldest request leaves the window). A `429` response (`code: rate_limited`) adds `Retry-After` with the same computed wait, never a flat 60 s, and echoes it as `retry_after`. Limits are enforced per API instance, so a multi-instance deployment may admit slightly more than the documented rate.\n\n## Which key do I need?\n\nTradewink has two APIs with two key systems. Using one key against the other API returns `401` or `403`.\n\n| API | Host | Key | Where to get it | Plans |\n|-----|------|-----|-----------------|-------|\n| REST API (`/v1/...`) | `https://tradewink.fly.dev` | `tw_<64 hex>` | `/apikey generate` in Discord or `POST /v1/api-keys` | Pro and Elite |\n| Signals API (`/api/v1/signals...`) | `https://tradewink-signals.fly.dev` | Signals API key | tradewink.com/signals-dashboard/api-keys | Pro and Elite |\n\n## Errors\n\nBoth APIs return `{\"ok\": false, \"error\": <message>, \"code\": <code>}` on failure. Branch on `code` (see `ErrorResponse`), never on the `error` text. The Signals API also keeps FastAPI's `detail` field for clients written against it.",
    "contact": {
      "name": "Tradewink Support",
      "url": "https://tradewink.com"
    },
    "license": {
      "name": "Proprietary"
    }
  },
  "servers": [
    {
      "url": "https://tradewink.fly.dev",
      "description": "Public Tradewink REST API"
    }
  ],
  "security": [
    {
      "BearerAuth": []
    }
  ],
  "tags": [
    {
      "name": "Account",
      "description": "Broker account information"
    },
    {
      "name": "Watchlist",
      "description": "Manage watched tickers"
    },
    {
      "name": "Quotes",
      "description": "Real-time and historical market data"
    },
    {
      "name": "Chart Patterns",
      "description": "Beta: read-only classical chart and candlestick pattern scans on daily bars"
    },
    {
      "name": "Portfolio",
      "description": "Portfolio positions and analysis"
    },
    {
      "name": "Strategies",
      "description": "AI-generated trading strategies"
    },
    {
      "name": "Exit Strategies",
      "description": "Scale-out profit-taking plans"
    },
    {
      "name": "Alerts",
      "description": "Recent alerts for watchlist tickers"
    },
    {
      "name": "Sell Confidence",
      "description": "Evidence-based selling assessments"
    },
    {
      "name": "Insights",
      "description": "Autonomous AI thinking insights"
    },
    {
      "name": "Sentiment & News",
      "description": "AI sentiment scores and news summaries"
    },
    {
      "name": "Analyst",
      "description": "Analyst ratings and price targets"
    },
    {
      "name": "Earnings",
      "description": "Upcoming earnings information"
    },
    {
      "name": "Market Intelligence",
      "description": "Move explanations, plain-English screens, thematic baskets, relative strength"
    },
    {
      "name": "Bots",
      "description": "Grid and recurring-buy (DCA) bot backtests"
    },
    {
      "name": "Trades",
      "description": "Trade journal and performance analytics"
    },
    {
      "name": "Agent",
      "description": "Autonomous agent status"
    },
    {
      "name": "API Keys",
      "description": "Manage your REST API keys"
    },
    {
      "name": "Admin",
      "description": "Agent configuration and API statistics"
    },
    {
      "name": "Signals",
      "description": "Published signal objects from the Signals API (separate host and key; Pro and Elite)"
    }
  ],
  "components": {
    "securitySchemes": {
      "BearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "API Key",
        "description": "API key generated via `/apikey generate` Discord command or `POST /v1/api-keys`. Format: `tw_<64 hex chars>`"
      },
      "SignalsBearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "Signals API Key",
        "description": "Signals API key from tradewink.com/signals-dashboard/api-keys (Pro and Elite). Not interchangeable with the `tw_` REST API key."
      }
    },
    "schemas": {
      "SuccessResponse": {
        "type": "object",
        "properties": {
          "ok": {
            "type": "boolean",
            "const": true
          },
          "data": {
            "description": "Response payload"
          }
        },
        "required": [
          "ok",
          "data"
        ]
      },
      "ErrorResponse": {
        "type": "object",
        "properties": {
          "ok": {
            "type": "boolean",
            "const": false
          },
          "error": {
            "type": "string",
            "description": "Human-readable error message; may change, do not branch on it"
          },
          "code": {
            "type": "string",
            "enum": [
              "bad_request",
              "invalid_ticker",
              "unauthorized",
              "forbidden",
              "forbidden_tier",
              "not_found",
              "rate_limited",
              "internal_error",
              "upstream_unavailable",
              "service_unavailable"
            ],
            "description": "Stable machine-readable error code"
          },
          "detail": {
            "description": "Signals API only: the same message in FastAPI's legacy field"
          }
        },
        "required": [
          "ok",
          "error",
          "code"
        ]
      },
      "SignalObject": {
        "type": "object",
        "description": "A row of `trading_signals`. Advisory types (market_outlook, macro_alert, exit_alert, weekly_digest) carry placeholder levels: do not render their stop, target or risk_reward as a trade setup.",
        "properties": {
          "id": {
            "type": "string"
          },
          "ticker": {
            "type": "string"
          },
          "signal_type": {
            "type": "string",
            "description": "Currently published values: momentum_alert, market_outlook, exit_alert, macro_alert, weekly_digest. Other values appear only on historical rows."
          },
          "direction": {
            "type": "string",
            "enum": [
              "bullish",
              "bearish",
              "neutral"
            ]
          },
          "confidence": {
            "type": "number",
            "description": "0-100 setup score: how much support the setup has, adjusted against how similar past signals resolved. Not a forecast or a win probability."
          },
          "timeframe": {
            "type": "string"
          },
          "entry_low": {
            "type": "number"
          },
          "entry_high": {
            "type": "number"
          },
          "stop_loss": {
            "type": "number"
          },
          "target_price": {
            "type": "number"
          },
          "risk_reward": {
            "type": "number"
          },
          "analysis": {
            "type": "string",
            "description": "Written reasoning"
          },
          "catalysts": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "outcome": {
            "type": "string",
            "enum": [
              "active",
              "win",
              "loss",
              "expired",
              "cancelled",
              "advisory"
            ]
          },
          "outcome_pnl_pct": {
            "type": "number",
            "nullable": true
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "expires_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          }
        },
        "additionalProperties": true
      },
      "Ticker": {
        "type": "string",
        "pattern": "^[A-Z]{1,5}(\\.[A-Z])?$",
        "example": "AAPL",
        "description": "US stock ticker symbol (1-5 uppercase letters, optional .X suffix)"
      },
      "WatchlistResponse": {
        "type": "object",
        "properties": {
          "tickers": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Ticker"
            }
          },
          "count": {
            "type": "integer"
          }
        }
      },
      "AddWatchlistRequest": {
        "type": "object",
        "properties": {
          "ticker": {
            "$ref": "#/components/schemas/Ticker"
          }
        },
        "required": [
          "ticker"
        ]
      },
      "QuoteResponse": {
        "type": "object",
        "description": "Real-time quote data. Shape depends on the data provider.",
        "properties": {
          "price": {
            "type": "number"
          },
          "change": {
            "type": "number"
          },
          "change_percent": {
            "type": "number"
          },
          "volume": {
            "type": "integer"
          },
          "high": {
            "type": "number"
          },
          "low": {
            "type": "number"
          },
          "open": {
            "type": "number"
          },
          "previous_close": {
            "type": "number"
          }
        }
      },
      "HistoricalBar": {
        "type": "object",
        "properties": {
          "Date": {
            "type": "string",
            "format": "date-time"
          },
          "Open": {
            "type": "number"
          },
          "High": {
            "type": "number"
          },
          "Low": {
            "type": "number"
          },
          "Close": {
            "type": "number"
          },
          "Volume": {
            "type": "integer"
          }
        }
      },
      "PortfolioPosition": {
        "type": "object",
        "properties": {
          "ticker": {
            "type": "string"
          },
          "shares": {
            "type": "number"
          },
          "avg_price": {
            "type": "number"
          },
          "current_price": {
            "type": "number"
          },
          "pnl": {
            "type": "number"
          },
          "pnl_pct": {
            "type": "number"
          }
        }
      },
      "Strategy": {
        "type": "object",
        "description": "AI-generated trading strategy",
        "properties": {
          "id": {
            "type": "integer"
          },
          "ticker": {
            "type": "string"
          },
          "direction": {
            "type": "string",
            "enum": [
              "buy",
              "sell"
            ]
          },
          "entry_price": {
            "type": "number"
          },
          "stop_loss": {
            "type": "number"
          },
          "target_price": {
            "type": "number"
          },
          "confidence": {
            "type": "number"
          },
          "reasoning": {
            "type": "string"
          },
          "status": {
            "type": "string"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "TradeAnalyticsSummary": {
        "type": "object",
        "properties": {
          "total_trades": {
            "type": "integer"
          },
          "total_pnl": {
            "type": "number"
          },
          "win_count": {
            "type": "integer"
          },
          "loss_count": {
            "type": "integer"
          },
          "win_rate": {
            "type": "number",
            "description": "Win rate as percentage (0-100)"
          },
          "avg_win": {
            "type": "number"
          },
          "avg_loss": {
            "type": "number"
          },
          "profit_factor": {
            "type": "number"
          }
        }
      },
      "APIKeyInfo": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer"
          },
          "key_prefix": {
            "type": "string",
            "example": "tw_a1b2c3d4..."
          },
          "name": {
            "type": "string"
          },
          "permissions": {
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "read",
                "trade",
                "admin"
              ]
            }
          },
          "rate_limit_per_minute": {
            "type": "integer"
          },
          "is_active": {
            "type": "boolean"
          },
          "last_used_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "request_count": {
            "type": "integer"
          },
          "expires_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "CreateAPIKeyRequest": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "default": "default",
            "description": "A label for this key"
          },
          "permissions": {
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "read",
                "trade"
              ]
            },
            "default": [
              "read"
            ],
            "description": "Permission scopes for the key. Admin keys cannot be minted here."
          }
        }
      },
      "CreateAPIKeyResponse": {
        "type": "object",
        "properties": {
          "key": {
            "type": "string",
            "description": "The raw API key. Shown once \u2014 store it securely."
          },
          "prefix": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "message": {
            "type": "string"
          }
        }
      },
      "Period": {
        "type": "string",
        "enum": [
          "1d",
          "5d",
          "1mo",
          "3mo",
          "6mo",
          "1y",
          "2y",
          "5y"
        ],
        "default": "1mo"
      },
      "ChartPattern": {
        "type": "object",
        "description": "One detected classical chart pattern. Heuristic detection on closing prices; not a forecast.",
        "properties": {
          "name": {
            "type": "string",
            "enum": [
              "double_top",
              "double_bottom",
              "head_and_shoulders",
              "inverse_head_and_shoulders",
              "triangle",
              "flag",
              "cup_and_handle",
              "rounding_bottom",
              "wedge"
            ]
          },
          "direction": {
            "type": "string",
            "enum": [
              "bullish",
              "bearish",
              "neutral"
            ]
          },
          "confidence": {
            "type": "number",
            "minimum": 0,
            "maximum": 1,
            "description": "Heuristic cleanliness score of the structure, not a probability of success"
          },
          "key_levels": {
            "type": "object",
            "additionalProperties": {
              "type": "number"
            },
            "description": "Labelled price levels (neckline, peaks, troughs, trendlines)"
          },
          "target": {
            "type": "number",
            "nullable": true,
            "description": "Measured-move level implied by the pattern"
          }
        },
        "required": [
          "name",
          "direction",
          "confidence",
          "key_levels"
        ]
      },
      "CandlestickPattern": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string"
          },
          "direction": {
            "type": "string",
            "enum": [
              "bullish",
              "bearish",
              "neutral"
            ]
          },
          "strength": {
            "type": "number",
            "minimum": 0,
            "maximum": 1
          }
        },
        "required": [
          "name",
          "direction",
          "strength"
        ]
      },
      "PatternScanResponse": {
        "type": "object",
        "description": "Beta. Read-only scan output; it does not change signal scoring or create signals.",
        "properties": {
          "ticker": {
            "type": "string"
          },
          "period": {
            "$ref": "#/components/schemas/Period"
          },
          "beta": {
            "type": "boolean",
            "const": true
          },
          "bars": {
            "type": "integer",
            "description": "Daily bars scanned"
          },
          "chart_patterns": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ChartPattern"
            }
          },
          "candlestick_patterns": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/CandlestickPattern"
            },
            "description": "Candle patterns on the most recent bars; empty when OHLC columns are missing"
          },
          "candlestick_bias": {
            "type": "object",
            "nullable": true,
            "properties": {
              "net_bias": {
                "type": "string",
                "enum": [
                  "bullish",
                  "bearish",
                  "neutral"
                ]
              },
              "net_strength": {
                "type": "number",
                "minimum": 0,
                "maximum": 1
              }
            }
          },
          "insufficient_bars": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Chart detectors skipped because the series is shorter than their minimum"
          }
        },
        "required": [
          "ticker",
          "period",
          "beta",
          "bars",
          "chart_patterns",
          "candlestick_patterns"
        ]
      }
    },
    "headers": {
      "X-RateLimit-Limit": {
        "description": "Requests allowed per 60-second sliding window for this key",
        "schema": {
          "type": "integer",
          "minimum": 0
        }
      },
      "X-RateLimit-Remaining": {
        "description": "Requests left in the current window (0 when limited)",
        "schema": {
          "type": "integer",
          "minimum": 0
        }
      },
      "X-RateLimit-Reset": {
        "description": "Seconds until the oldest request in the window expires and a slot frees",
        "schema": {
          "type": "integer",
          "minimum": 0,
          "maximum": 60
        }
      },
      "Retry-After": {
        "description": "Seconds to wait before retrying, computed from the sliding window (1-60)",
        "schema": {
          "type": "integer",
          "minimum": 1,
          "maximum": 60
        }
      }
    },
    "responses": {
      "RateLimited": {
        "description": "Per-key rate limit exceeded (`code: rate_limited`). Wait `Retry-After` seconds.",
        "headers": {
          "Retry-After": {
            "$ref": "#/components/headers/Retry-After"
          },
          "X-RateLimit-Limit": {
            "$ref": "#/components/headers/X-RateLimit-Limit"
          },
          "X-RateLimit-Remaining": {
            "$ref": "#/components/headers/X-RateLimit-Remaining"
          },
          "X-RateLimit-Reset": {
            "$ref": "#/components/headers/X-RateLimit-Reset"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "allOf": [
                {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                {
                  "type": "object",
                  "properties": {
                    "limit": {
                      "type": "integer"
                    },
                    "window": {
                      "type": "string",
                      "const": "60s"
                    },
                    "retry_after": {
                      "type": "integer",
                      "minimum": 1,
                      "maximum": 60
                    }
                  }
                }
              ]
            }
          }
        }
      }
    }
  },
  "paths": {
    "/v1/account": {
      "get": {
        "tags": [
          "Account"
        ],
        "summary": "Get account info",
        "description": "Returns broker account information including balance and buying power.",
        "operationId": "getAccount",
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Account information",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuccessResponse"
                }
              }
            },
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              }
            }
          },
          "401": {
            "description": "Invalid or missing API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "No broker configured for this user"
          },
          "502": {
            "description": "Broker communication error"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/v1/watchlist": {
      "get": {
        "tags": [
          "Watchlist"
        ],
        "summary": "List watchlist",
        "description": "Returns all tickers on your watchlist.",
        "operationId": "getWatchlist",
        "responses": {
          "200": {
            "description": "Watchlist tickers",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/SuccessResponse"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "$ref": "#/components/schemas/WatchlistResponse"
                        }
                      }
                    }
                  ]
                }
              }
            },
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              }
            }
          },
          "401": {
            "description": "Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "post": {
        "tags": [
          "Watchlist"
        ],
        "summary": "Add ticker to watchlist",
        "description": "Add a single ticker to your watchlist. Requires `trade` permission.",
        "operationId": "addWatchlist",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AddWatchlistRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Ticker added (or already present)",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              }
            }
          },
          "400": {
            "description": "Invalid ticker"
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Insufficient permissions (requires trade)"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/v1/watchlist/{ticker}": {
      "delete": {
        "tags": [
          "Watchlist"
        ],
        "summary": "Remove ticker from watchlist",
        "description": "Remove a ticker from your watchlist. Requires `trade` permission.",
        "operationId": "removeWatchlist",
        "parameters": [
          {
            "name": "ticker",
            "in": "path",
            "required": true,
            "schema": {
              "$ref": "#/components/schemas/Ticker"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Ticker removed",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              }
            }
          },
          "400": {
            "description": "Invalid ticker"
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Insufficient permissions"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/v1/quotes/{ticker}": {
      "get": {
        "tags": [
          "Quotes"
        ],
        "summary": "Get real-time quote",
        "description": "Returns the latest quote data for a ticker.",
        "operationId": "getQuote",
        "parameters": [
          {
            "name": "ticker",
            "in": "path",
            "required": true,
            "schema": {
              "$ref": "#/components/schemas/Ticker"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Quote data",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/SuccessResponse"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "$ref": "#/components/schemas/QuoteResponse"
                        }
                      }
                    }
                  ]
                }
              }
            },
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              }
            }
          },
          "400": {
            "description": "Invalid ticker"
          },
          "404": {
            "description": "No quote data available"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/v1/quotes/{ticker}/history": {
      "get": {
        "tags": [
          "Quotes"
        ],
        "summary": "Get historical OHLCV data",
        "description": "Returns historical price bars for a ticker over a given period.",
        "operationId": "getHistorical",
        "parameters": [
          {
            "name": "ticker",
            "in": "path",
            "required": true,
            "schema": {
              "$ref": "#/components/schemas/Ticker"
            }
          },
          {
            "name": "period",
            "in": "query",
            "required": false,
            "schema": {
              "$ref": "#/components/schemas/Period"
            },
            "description": "Time period for historical data"
          }
        ],
        "responses": {
          "200": {
            "description": "Historical bars",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/SuccessResponse"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "type": "object",
                          "properties": {
                            "ticker": {
                              "type": "string"
                            },
                            "period": {
                              "type": "string"
                            },
                            "bars": {
                              "type": "array",
                              "items": {
                                "$ref": "#/components/schemas/HistoricalBar"
                              }
                            }
                          }
                        }
                      }
                    }
                  ]
                }
              }
            },
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              }
            }
          },
          "400": {
            "description": "Invalid ticker or period"
          },
          "404": {
            "description": "No historical data"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/v1/patterns/{ticker}": {
      "get": {
        "tags": [
          "Chart Patterns"
        ],
        "summary": "Scan chart and candlestick patterns (beta)",
        "description": "**Beta.** Runs Tradewink's rule-based detectors over daily closing prices (double top/bottom, head and shoulders, triangle, flag, cup and handle, rounding bottom, wedge) and over the latest OHLC bars (single- and multi-candle patterns), returning each detected pattern with its direction, heuristic confidence and key price levels. Read-only: the scan does not change signal scoring, create signals or place orders, and a detected pattern is not a forecast. Most chart detectors need 30-60 bars, so `period` defaults to `6mo`. Also available as the `get_chart_patterns` MCP tool.",
        "operationId": "getChartPatterns",
        "parameters": [
          {
            "name": "ticker",
            "in": "path",
            "required": true,
            "schema": {
              "$ref": "#/components/schemas/Ticker"
            }
          },
          {
            "name": "period",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "1d",
                "5d",
                "1mo",
                "3mo",
                "6mo",
                "1y",
                "2y",
                "5y"
              ],
              "default": "6mo"
            },
            "description": "Daily-bar lookback to scan"
          }
        ],
        "responses": {
          "200": {
            "description": "Pattern scan",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/SuccessResponse"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "$ref": "#/components/schemas/PatternScanResponse"
                        }
                      }
                    }
                  ]
                }
              }
            },
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              }
            }
          },
          "400": {
            "description": "Invalid ticker or period"
          },
          "404": {
            "description": "No historical data"
          },
          "502": {
            "description": "Market data fetch or scan failed"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/v1/portfolio": {
      "get": {
        "tags": [
          "Portfolio"
        ],
        "summary": "Get portfolio positions",
        "description": "Returns current portfolio positions for the authenticated user.",
        "operationId": "getPortfolio",
        "responses": {
          "200": {
            "description": "Portfolio positions",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              }
            }
          },
          "401": {
            "description": "Unauthorized"
          },
          "503": {
            "description": "Portfolio tracker not available"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/v1/portfolio/live": {
      "get": {
        "tags": [
          "Portfolio"
        ],
        "summary": "Get live portfolio",
        "description": "Returns portfolio positions enriched with current market prices and live P&L.",
        "operationId": "getPortfolioLive",
        "responses": {
          "200": {
            "description": "Live portfolio with current prices",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              }
            }
          },
          "502": {
            "description": "Portfolio fetch error"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/v1/portfolio/analysis": {
      "get": {
        "tags": [
          "Portfolio"
        ],
        "summary": "Get portfolio analysis",
        "description": "Full technical analysis plus AI-generated trim/exit strategies for all positions.",
        "operationId": "getPortfolioAnalysis",
        "responses": {
          "200": {
            "description": "Portfolio analysis with per-position technicals and AI suggestions",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              }
            }
          },
          "502": {
            "description": "Analysis failed"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/v1/strategies": {
      "get": {
        "tags": [
          "Strategies"
        ],
        "summary": "List active strategies",
        "description": "Returns active AI-generated trading strategies. Optionally filter by ticker.",
        "operationId": "getStrategies",
        "parameters": [
          {
            "name": "ticker",
            "in": "query",
            "required": false,
            "schema": {
              "$ref": "#/components/schemas/Ticker"
            },
            "description": "Filter by ticker"
          }
        ],
        "responses": {
          "200": {
            "description": "Strategy list",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              }
            }
          },
          "400": {
            "description": "Invalid ticker parameter"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/v1/strategies/{id}": {
      "get": {
        "tags": [
          "Strategies"
        ],
        "summary": "Get strategy by ID",
        "description": "Returns a specific strategy by its database ID.",
        "operationId": "getStrategy",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Strategy details",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              }
            }
          },
          "400": {
            "description": "Invalid ID"
          },
          "404": {
            "description": "Strategy not found"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/v1/exit-strategies": {
      "get": {
        "tags": [
          "Exit Strategies"
        ],
        "summary": "List active exit strategies",
        "description": "Returns active exit strategies (scale-out profit-taking plans). Optionally filter by ticker.",
        "operationId": "getExitStrategies",
        "parameters": [
          {
            "name": "ticker",
            "in": "query",
            "required": false,
            "schema": {
              "$ref": "#/components/schemas/Ticker"
            },
            "description": "Filter by ticker"
          }
        ],
        "responses": {
          "200": {
            "description": "Exit strategy list",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              }
            }
          },
          "400": {
            "description": "Invalid ticker parameter"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/v1/alerts": {
      "get": {
        "tags": [
          "Alerts"
        ],
        "summary": "List recent alerts",
        "description": "Returns recent alerts for tickers in your watchlist.",
        "operationId": "getAlerts",
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 20
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Alert list",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/v1/sell-confidence": {
      "get": {
        "tags": [
          "Sell Confidence"
        ],
        "summary": "List active sell confidence assessments",
        "description": "Returns all active (latest) sell confidence assessments across the portfolio.",
        "operationId": "getSellConfidence",
        "responses": {
          "200": {
            "description": "Sell confidence assessments",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/v1/sell-confidence/{ticker}": {
      "get": {
        "tags": [
          "Sell Confidence"
        ],
        "summary": "Get sell confidence for a ticker",
        "description": "Returns sell confidence history for a specific ticker.",
        "operationId": "getSellConfidenceTicker",
        "parameters": [
          {
            "name": "ticker",
            "in": "path",
            "required": true,
            "schema": {
              "$ref": "#/components/schemas/Ticker"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Sell confidence for ticker",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              }
            }
          },
          "400": {
            "description": "Invalid ticker"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/v1/insights": {
      "get": {
        "tags": [
          "Insights"
        ],
        "summary": "List thinking insights",
        "description": "Returns recent autonomous AI thinking insights (cross-watchlist correlations, sector rotation, risk clusters).",
        "operationId": "getInsights",
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 50,
              "default": 10
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Thinking insights",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/v1/sentiment/{ticker}": {
      "get": {
        "tags": [
          "Sentiment & News"
        ],
        "summary": "Get AI sentiment score",
        "description": "Returns an AI-generated sentiment score based on recent news headlines.",
        "operationId": "getSentiment",
        "parameters": [
          {
            "name": "ticker",
            "in": "path",
            "required": true,
            "schema": {
              "$ref": "#/components/schemas/Ticker"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Sentiment analysis result",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              }
            }
          },
          "400": {
            "description": "Invalid ticker"
          },
          "502": {
            "description": "Sentiment analysis failed"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/v1/news/{ticker}": {
      "get": {
        "tags": [
          "Sentiment & News"
        ],
        "summary": "Get news with AI summaries",
        "description": "Returns recent news articles with AI-generated summaries for a ticker.",
        "operationId": "getNews",
        "parameters": [
          {
            "name": "ticker",
            "in": "path",
            "required": true,
            "schema": {
              "$ref": "#/components/schemas/Ticker"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "News articles with summaries",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              }
            }
          },
          "400": {
            "description": "Invalid ticker"
          },
          "502": {
            "description": "News fetch failed"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/v1/analyst/{ticker}": {
      "get": {
        "tags": [
          "Analyst"
        ],
        "summary": "Get analyst ratings",
        "description": "Returns analyst recommendation trends and price targets.",
        "operationId": "getAnalystRatings",
        "parameters": [
          {
            "name": "ticker",
            "in": "path",
            "required": true,
            "schema": {
              "$ref": "#/components/schemas/Ticker"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Analyst ratings and price targets",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              }
            }
          },
          "400": {
            "description": "Invalid ticker"
          },
          "404": {
            "description": "No analyst data"
          },
          "502": {
            "description": "Data fetch failed"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/v1/earnings/{ticker}": {
      "get": {
        "tags": [
          "Earnings"
        ],
        "summary": "Get upcoming earnings",
        "description": "Returns upcoming earnings date and estimates for a ticker.",
        "operationId": "getEarnings",
        "parameters": [
          {
            "name": "ticker",
            "in": "path",
            "required": true,
            "schema": {
              "$ref": "#/components/schemas/Ticker"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Earnings information",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              }
            }
          },
          "400": {
            "description": "Invalid ticker"
          },
          "404": {
            "description": "No earnings data"
          },
          "502": {
            "description": "Data fetch failed"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/v1/earnings/{ticker}/moves": {
      "get": {
        "tags": [
          "Earnings"
        ],
        "summary": "Earnings implied vs historical move",
        "description": "Options-implied earnings move (ATM straddle, IV fallback) compared with the stock's historical post-earnings reactions, plus EPS beat rate. Every number is computed from market data; missing sources are listed in data_gaps.",
        "operationId": "getEarningsMoves",
        "parameters": [
          {
            "name": "ticker",
            "in": "path",
            "required": true,
            "schema": {
              "$ref": "#/components/schemas/Ticker"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Earnings move report",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              }
            }
          },
          "400": {
            "description": "Invalid ticker"
          },
          "502": {
            "description": "Analysis failed"
          },
          "503": {
            "description": "Feature disabled"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/v1/why/{ticker}": {
      "get": {
        "tags": [
          "Market Intelligence"
        ],
        "summary": "Why is it moving?",
        "description": "Explains today's move: beta-expected market component vs stock-specific residual, sector move, move z-score, relative volume, and catalysts (news, earnings, analyst actions), with a short summary. Stocks and crypto pairs (e.g. BTC-USD).",
        "operationId": "getMoveExplanation",
        "parameters": [
          {
            "name": "ticker",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "example": "NVDA"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Move explanation",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              }
            }
          },
          "400": {
            "description": "Invalid ticker"
          },
          "502": {
            "description": "Explanation failed"
          },
          "503": {
            "description": "Feature disabled"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/v1/themes": {
      "post": {
        "tags": [
          "Market Intelligence"
        ],
        "summary": "Build a thematic basket from a prompt",
        "description": "Turns a plain-English theme into a weighted basket of US-listed stocks/ETFs and backtests it against a benchmark. Holdings are chosen today, so the backtest is hindsight-biased and not an out-of-sample test (disclosed in hindsight_warning).",
        "operationId": "createThemeBasket",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "prompt"
                ],
                "properties": {
                  "prompt": {
                    "type": "string",
                    "maxLength": 200,
                    "example": "nuclear power"
                  },
                  "weighting": {
                    "type": "string",
                    "enum": [
                      "equal",
                      "inverse_vol",
                      "market_cap"
                    ]
                  },
                  "max_holdings": {
                    "type": "integer",
                    "minimum": 3,
                    "maximum": 20
                  },
                  "lookback": {
                    "type": "string",
                    "enum": [
                      "3mo",
                      "6mo",
                      "1y",
                      "2y",
                      "5y"
                    ]
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Basket with holdings, weights, and backtest statistics",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              }
            }
          },
          "400": {
            "description": "Invalid request"
          },
          "422": {
            "description": "A basket could not be built for this theme"
          },
          "503": {
            "description": "Feature disabled"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/v1/rotation": {
      "get": {
        "tags": [
          "Market Intelligence"
        ],
        "summary": "Sector rotation (RRG)",
        "description": "Relative Rotation Graph coordinates (RS-Ratio / RS-Momentum approximation, weekly) and quadrant for the 11 SPDR sector ETFs, or up to 20 custom tickers.",
        "operationId": "getSectorRotation",
        "parameters": [
          {
            "name": "benchmark",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "default": "SPY"
            }
          },
          {
            "name": "tickers",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "example": "XLK,XLF,XLE"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "RRG points by ticker",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              }
            }
          },
          "400": {
            "description": "Invalid benchmark or tickers"
          },
          "503": {
            "description": "Feature disabled"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/v1/relative-strength/{ticker}": {
      "get": {
        "tags": [
          "Market Intelligence"
        ],
        "summary": "RS rating, trend template, stage",
        "description": "IBD-style RS rating (1-99 percentile of weighted 12-month performance vs Tradewink's liquid universe), Minervini Trend Template criteria, and Weinstein stage.",
        "operationId": "getRelativeStrength",
        "parameters": [
          {
            "name": "ticker",
            "in": "path",
            "required": true,
            "schema": {
              "$ref": "#/components/schemas/Ticker"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Relative strength report",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              }
            }
          },
          "400": {
            "description": "Invalid ticker"
          },
          "503": {
            "description": "Feature disabled"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/v1/trades": {
      "get": {
        "tags": [
          "Trades"
        ],
        "summary": "List trade journal entries",
        "description": "Returns trade journal entries for the authenticated user.",
        "operationId": "getTrades",
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 200,
              "default": 50
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Trade journal entries",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/v1/trades/analytics": {
      "get": {
        "tags": [
          "Trades"
        ],
        "summary": "Get trade analytics",
        "description": "Returns trade performance analytics with summary statistics (win rate, profit factor, avg P&L).",
        "operationId": "getTradeAnalytics",
        "responses": {
          "200": {
            "description": "Trade analytics with summary",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/SuccessResponse"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "type": "object",
                          "properties": {
                            "trades": {
                              "type": "array",
                              "items": {
                                "type": "object"
                              }
                            },
                            "summary": {
                              "$ref": "#/components/schemas/TradeAnalyticsSummary"
                            }
                          }
                        }
                      }
                    }
                  ]
                }
              }
            },
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/v1/agent/status": {
      "get": {
        "tags": [
          "Agent"
        ],
        "summary": "Get agent status",
        "description": "Returns the current status of all autonomous agent loops.",
        "operationId": "getAgentStatus",
        "responses": {
          "200": {
            "description": "Agent loop statuses",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              }
            }
          },
          "503": {
            "description": "Agent not running"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/v1/api-keys": {
      "get": {
        "tags": [
          "API Keys"
        ],
        "summary": "List your API keys",
        "description": "Returns all active API keys for the authenticated user. Does not expose key hashes.",
        "operationId": "listApiKeys",
        "responses": {
          "200": {
            "description": "API key list",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/SuccessResponse"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "type": "object",
                          "properties": {
                            "keys": {
                              "type": "array",
                              "items": {
                                "$ref": "#/components/schemas/APIKeyInfo"
                              }
                            }
                          }
                        }
                      }
                    }
                  ]
                }
              }
            },
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "post": {
        "tags": [
          "API Keys"
        ],
        "summary": "Create a new API key",
        "description": "Generates a new API key. The raw key is returned **once** in the response \u2014 store it securely. Requires `trade` permission. Max 5 active keys per user.",
        "operationId": "createApiKey",
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateAPIKeyRequest"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "API key created",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/SuccessResponse"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "$ref": "#/components/schemas/CreateAPIKeyResponse"
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Invalid permissions or max keys reached"
          },
          "403": {
            "description": "Insufficient permissions (requires trade)"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/v1/api-keys/{id}": {
      "delete": {
        "tags": [
          "API Keys"
        ],
        "summary": "Revoke an API key",
        "description": "Revokes an API key by its ID. The key will immediately stop working. Requires `trade` permission.",
        "operationId": "revokeApiKey",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "API key ID (from /v1/api-keys list)"
          }
        ],
        "responses": {
          "200": {
            "description": "Key revoked",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              }
            }
          },
          "400": {
            "description": "Invalid key ID"
          },
          "403": {
            "description": "Insufficient permissions"
          },
          "404": {
            "description": "Key not found or already revoked"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/v1/admin/watchlists": {
      "get": {
        "tags": [
          "Admin"
        ],
        "summary": "Get all watchlists",
        "description": "Returns all users' watchlists grouped by user. Requires `admin` permission.",
        "operationId": "getAllWatchlists",
        "responses": {
          "200": {
            "description": "All watchlists"
          },
          "403": {
            "description": "Requires admin permission"
          },
          "502": {
            "description": "Failed to fetch watchlists"
          }
        }
      }
    },
    "/v1/admin/agent/config": {
      "get": {
        "tags": [
          "Admin"
        ],
        "summary": "Get agent configuration",
        "description": "Returns the current agent loop configuration (enabled/disabled states). Requires `admin` permission.",
        "operationId": "getAgentConfig",
        "responses": {
          "200": {
            "description": "Agent configuration"
          },
          "403": {
            "description": "Requires admin permission"
          },
          "503": {
            "description": "Agent not running"
          }
        }
      }
    },
    "/v1/admin/stats": {
      "get": {
        "tags": [
          "Admin"
        ],
        "summary": "Get API usage statistics",
        "description": "Returns API usage statistics including total requests, rejections, and tracked keys. Requires `admin` permission.",
        "operationId": "getApiStats",
        "responses": {
          "200": {
            "description": "API statistics"
          },
          "403": {
            "description": "Requires admin permission"
          }
        }
      }
    },
    "/api/v1/signals": {
      "servers": [
        {
          "url": "https://tradewink-signals.fly.dev",
          "description": "Tradewink Signals API"
        }
      ],
      "get": {
        "tags": [
          "Signals"
        ],
        "summary": "List published signals",
        "description": "Returns the most recent published signal objects, newest first. Requires a Signals API key on an active Pro or Elite subscription.",
        "operationId": "listSignals",
        "security": [
          {
            "SignalsBearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 20
            }
          },
          {
            "name": "signal_type",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "Filter by type. Currently published: momentum_alert, market_outlook, exit_alert, macro_alert, weekly_digest."
          },
          {
            "name": "ticker",
            "in": "query",
            "schema": {
              "$ref": "#/components/schemas/Ticker"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Signal objects",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "signals": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/SignalObject"
                      }
                    }
                  },
                  "required": [
                    "signals"
                  ]
                }
              }
            }
          },
          "401": {
            "description": "Missing or malformed Signals API key (`code: unauthorized`)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Key not linked to an active Pro or Elite subscriber (`code: forbidden` or `forbidden_tier`)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "503": {
            "description": "Signals database unavailable (`code: service_unavailable`)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/signals/{signal_id}": {
      "servers": [
        {
          "url": "https://tradewink-signals.fly.dev",
          "description": "Tradewink Signals API"
        }
      ],
      "get": {
        "tags": [
          "Signals"
        ],
        "summary": "Get one signal",
        "operationId": "getSignal",
        "security": [
          {
            "SignalsBearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "signal_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Signal object",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "signal": {
                      "$ref": "#/components/schemas/SignalObject"
                    }
                  },
                  "required": [
                    "signal"
                  ]
                }
              }
            }
          },
          "401": {
            "description": "Missing or malformed key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Not a Pro or Elite subscriber",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Signal not found (`code: not_found`)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/signals/performance/summary": {
      "servers": [
        {
          "url": "https://tradewink-signals.fly.dev",
          "description": "Tradewink Signals API"
        }
      ],
      "get": {
        "tags": [
          "Signals"
        ],
        "summary": "Signal performance summary",
        "description": "Per-type resolved-outcome aggregates from the published evaluation methodology. Not a forecast of future results.",
        "operationId": "getSignalPerformanceSummary",
        "security": [
          {
            "SignalsBearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Performance rows",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "performance": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "additionalProperties": true
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or malformed key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Not a Pro or Elite subscriber",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/market/regime": {
      "servers": [
        {
          "url": "https://tradewink-signals.fly.dev",
          "description": "Tradewink Signals API"
        }
      ],
      "get": {
        "tags": [
          "Signals"
        ],
        "summary": "Current market regime",
        "description": "The regime label the signal engine is using right now (SPY-based HMM detector), with its confidence and the time it was last detected.",
        "operationId": "getMarketRegime",
        "security": [
          {
            "SignalsBearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Regime state",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "regime": {
                      "type": "string",
                      "enum": [
                        "bull",
                        "sideways",
                        "bear",
                        "unknown"
                      ]
                    },
                    "confidence": {
                      "type": "number",
                      "minimum": 0,
                      "maximum": 1
                    },
                    "probabilities": {
                      "type": "object",
                      "additionalProperties": {
                        "type": "number"
                      }
                    },
                    "as_of": {
                      "type": "string",
                      "format": "date-time",
                      "nullable": true
                    }
                  },
                  "required": [
                    "regime",
                    "confidence"
                  ]
                }
              }
            }
          },
          "401": {
            "description": "Missing or malformed key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Not a Pro or Elite subscriber",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "503": {
            "description": "No regime detected yet (`code: service_unavailable`)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/feed/signals.xml": {
      "servers": [
        {
          "url": "https://tradewink-signals.fly.dev",
          "description": "Tradewink Signals API"
        }
      ],
      "get": {
        "tags": [
          "Signals"
        ],
        "summary": "Private Atom feed of delivered signals",
        "description": "The caller's last 50 delivered signals as Atom, for feed readers that cannot set headers. The Signals API key travels in the `key` query parameter; keep the URL private.",
        "operationId": "getSignalFeed",
        "security": [],
        "parameters": [
          {
            "name": "key",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "minLength": 16,
              "maxLength": 256
            },
            "description": "Signals API key"
          }
        ],
        "responses": {
          "200": {
            "description": "Atom feed",
            "content": {
              "application/atom+xml": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "401": {
            "description": "Invalid key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Not a Pro or Elite subscriber",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    }
  }
}
